You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何自定义SwiftUI中List内NavigationLink的按压样式?

自定义Safari应用扩展中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中设置的自定义AccentColor
  • Color.accentColor 是系统默认强调色(macOS默认蓝色),若显示深灰色,可能是项目配置或Safari扩展环境限制导致。

若想统一修改List行默认高亮颜色,可通过List的tint修饰符:

List(filteredFolders) { folder in
    // ...
}
.tint(Color.gray.opacity(0.2)) // 修改按压时的高亮颜色

内容的提问来源于stack exchange,提问作者Luana Escobar Benito

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:53:16