SwiftUI列表与NavigationLink如何在轻触时进行自定义高亮显示

5

我如何在列表行上制作自定义覆盖层以在轻击时突出显示它。我正在使用NaviagationLink并已更改

UITableViewCell.appearance().cellSelectionStyle = .none 

为了避免使用默认的灰色选择,我尝试在我的单元格中添加一些 `@State isSelected`,但我不知道如何/何时更改此设置以获得所需的效果。我尝试添加 `onTapGesture()`,但它会阻止 NavigationLink,并且不提供开始、结束状态,只有结束状态。
3个回答

9
我用这种方式做:
List {
    ForEach(0..<self.contacts.count) { i in
       ZStack {
           NavigationLink(destination: ContactDetails(contact: self.contacts[i])) {
                EmptyView()
           }.opacity(0)

           Button(action: {}) {
                   ContactRow(contact: self.contacts[i])
           }.buttonStyle(ListButtonStyle())
        }.listRowBackground( (i%2 == 0) ? Color("DarkRowBackground") : .white)
           .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
     }
}

而且 ButtonStyle

struct ListButtonStyle: ButtonStyle {

    func makeBody(configuration: Self.Configuration) -> some View {

        configuration.label
            .overlay(configuration.isPressed ? Color("Dim").opacity(0.4) : Color.clear)
    }
}

我尝试了这种方法,它禁用了 NavigationLink 的跳转到详情页。如果不使用 Button ,它就可以正常工作。 - user2619824
您可以直接在 NavigationLink 上添加 buttonStyle。 - Bin Chen
使用.background而不是.overlay,因为如果使用.overlay,则颜色会添加并与标题混合。 - StackUnderflow

3

好的,这里是一个非常简单的演示方法。思路是将NavigationLink排除在List之外,并在行点击时手动激活它(这样可以轻松地进行点击而不需要在导航链接内部)......其他所有内容,如动画、效果和高亮类型都由您自己决定。

import SwiftUI
import UIKit

struct TestCustomCellHighlight: View {
    @State var selection: Int = -1
    @State var highlight = false
    @State var showDetails = false

    init() {
        UITableViewCell.appearance().selectionStyle = .none
    }

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: Text("Details \(self.selection)"), isActive: $showDetails) {
                    EmptyView()
                }
                List(0..<20, id: \.self) { i in
                    HStack {
                        Text("Item \(i)")
                        Spacer()
                    }
                    .padding(.vertical, 6)
                    .background(Color.white) // to be tappable row-wide
                    .overlay(self.highlightView(for: i))
                    .onTapGesture {
                        self.selection = i
                        self.highlight = true

                        // delay link activation to see selection effect
                        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                            self.highlight = false
                            self.showDetails = true
                        }
                    }
                }
            }
        }
    }

    private func highlightView(for index: Int) -> AnyView {
        if self.highlight && self.selection == index {
            return AnyView(Rectangle().inset(by: -5).fill(Color.red.opacity(0.5)))
        } else {
            return AnyView(EmptyView())
        }
    }
}

struct TestCustomCellHighlight_Previews: PreviewProvider {
    static var previews: some View {
        TestCustomCellHighlight()
    }
}

还不错,但现在我考虑实施这种方法 https://dev59.com/sFMI5IYBdhLWcg3wUZyd#56980172 - Michał Ziobro

0

对我来说,@Michał Ziobro答案的这个稍微改变了的版本最好:

struct DemoView: View {
    let listNames = ["Navigation1", "Navigation2", "Navigation3"]
    var body: some View {
        NavigationView {
            List {
                ForEach(listNames, id: \.self) { listName in
                    ZStack {
                        // NavigationLink tint is applied to invisible EmptyView
                        NavigationLink(destination: Text(listName)) {
                            EmptyView()
                        }.opacity(0)
                        // Button handles tint selection
                        Button(action: {}) {
                            Text(listName) // displayed list item (cell)
                        }
                    }
                }
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接