如何自定义SwiftUI中List内NavigationLink的按压样式?
我是一名编程初学者,正在开发Safari应用扩展作为我的第一个项目。我想要自定义List中NavigationLink行的按压样式。当前按压每行时会显示深灰色高亮,我希望改成更平滑的效果。
相关视图代码分为HomeView和FolderRowView两个文件:
HomeView 代码片段
var body: some View { NavigationStack { VStack(alignment: .leading, spacing: 8) { HStack { Text("Explore") .font(.title2) Spacer() SortButton(isAscending: $isAscending) // Now properly updates sorting } SearchBar(text: $searchText) .padding(.vertical, 2) //below is the list with the NavigationLink List(filteredFolders) { folder in NavigationLink(destination: DummyFolderDetailView(folder: folder)) { FolderRowView(folder: folder) } } .clipShape(RoundedRectangle(cornerRadius: 8)) .navigationTitle("Folders") } } }
FolderRowView 组件代码
struct FolderRowView: View { let folder: Folder @State private var isHovering = false var body: some View { HStack { // Folder Icon Image(systemName: "folder.fill") .resizable() .scaledToFit() .frame(width: 18, height: 18) .foregroundStyle(.gray) Spacer() // Folder Name Text(folder.name) .font(.body) .frame(maxWidth: .infinity, alignment: .leading) // Chevron Icon Image(systemName: "chevron.right") .foregroundColor(.gray) } .padding(.vertical, 6) .padding(.horizontal, 10) // Added horizontal padding for space around content .background(isHovering ? Color.gray.opacity(0.1) : Color.clear) // Optional background change on hover .scaleEffect(isHovering ? 1.05 : 1.0) // Zoom-in effect .cornerRadius(8) .animation(.easeInOut(duration: 0.2), value: isHovering) // Smooth animation .onHover { hovering in isHovering = hovering } .contextMenu { //(...) } }
目前我已在组件文件中成功实现了行的悬停样式及动画,但自定义按压样式要复杂得多。我已尝试过以下方法:
- 在组件文件中使用
.onTapGesture结合@State private var isPressed = false,按压样式显示正常,但NavigationLink的跳转功能失效; - 创建包含
PressableStyle: ButtonStyle的CustomStyle组件文件,将其作为修饰符添加到NavigationLink上,但无效果; - 按照建议创建FolderRowLink组件,将FolderRowView嵌套在Button内,并嵌入隐藏的NavigationLink由Button触发,但同样出现按压样式正常但跳转失效的问题。
另外我了解到默认按压高亮颜色为accentColor,但遇到了问题:.foregroundStyle(.accent)会调用Assets.xcassets中的AccentColor,而.foregroundStyle(Color.accentColor)调用的是默认深灰色,且给NavigationLink添加样式修饰符也无效。
解决方案
要自定义NavigationLink的按压样式,核心是保留NavigationLink的原生跳转功能,同时覆盖其默认的按压高亮效果,可以通过以下两种方式实现:
方法1:自定义ButtonStyle并应用到NavigationLink
NavigationLink本质是Button的子类,可通过自定义ButtonStyle修改按压状态,适配macOS/Safari扩展环境:
// 自定义按压样式 struct CustomRowPressStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .background(configuration.isPressed ? Color.gray.opacity(0.2) : Color.clear) .cornerRadius(8) .scaleEffect(configuration.isPressed ? 0.98 : 1.0) .animation(.easeInOut(duration: 0.15), value: configuration.isPressed) } } // 在HomeView中应用样式 List(filteredFolders) { folder in NavigationLink(destination: DummyFolderDetailView(folder: folder)) { FolderRowView(folder: folder) } .buttonStyle(CustomRowPressStyle()) // 应用自定义样式 }
方法2:在FolderRowView内部处理按压状态,保留NavigationLink交互
在FolderRowView中添加isPressed状态,通过simultaneousGesture监听按压事件,既不覆盖跳转功能,又能自定义样式:
// 修改后的FolderRowView struct FolderRowView: View { let folder: Folder @State private var isHovering = false @State private var isPressed = false var body: some View { HStack { Image(systemName: "folder.fill") .resizable() .scaledToFit() .frame(width: 18, height: 18) .foregroundStyle(.gray) Text(folder.name) .font(.body) .frame(maxWidth: .infinity, alignment: .leading) Image(systemName: "chevron.right") .foregroundColor(.gray) } .padding(.vertical, 6) .padding(.horizontal, 10) .background( Group { if isPressed { Color.gray.opacity(0.2) } else if isHovering { Color.gray.opacity(0.1) } else { Color.clear } } ) .scaleEffect(isPressed ? 0.98 : (isHovering ? 1.05 : 1.0)) .cornerRadius(8) .animation(.easeInOut(duration: 0.15), value: isPressed) .animation(.easeInOut(duration: 0.2), value: isHovering) .onHover { hovering in isHovering = hovering } // 用simultaneousGesture避免覆盖NavigationLink的点击事件 .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in isPressed = true } .onEnded { _ in isPressed = false } ) .contextMenu { // 你的上下文菜单内容 } } }
关于AccentColor的说明
在SwiftUI中:
.foregroundStyle(.accent)会引用Assets.xcassets中设置的自定义AccentColorColor.accentColor是系统默认强调色(macOS默认蓝色),若显示深灰色,可能是项目配置或Safari扩展环境限制导致。
若想统一修改List行默认高亮颜色,可通过List的tint修饰符:
List(filteredFolders) { folder in // ... } .tint(Color.gray.opacity(0.2)) // 修改按压时的高亮颜色
内容的提问来源于stack exchange,提问作者Luana Escobar Benito
相关产品推荐
相关产品推荐

