如何使用SwiftUI取消列表项在点击后的高亮显示?

64

如何使用SwiftUI在列表项上移除高亮显示?

List {

}.whatModifierToAddHere?

选择管理器文档中没有任何关于此的说明。


有找到前进的道路吗?我也卡在这里了! - Charlie
3
我最终使用了ScrollView而不是List。ScrollView类似于一个简单的列表,你需要自己添加所有额外的元素,但这对我来说很好。 - Just a coder
2
@Justacoder 我也遇到了一个滚动视图的相同问题(iOS 14)。在这个帖子里到处都提到的.buttonStyle(PlainButtonStyle())“解决方案”并不起作用。它只是减少了高亮效果,无论是在滚动还是普通按钮点击时。 - Martin
1
我刚刚花了大约两天的时间,首先推断出这是问题所在,然后尝试各种方法来解决它。我使用的是Xcode 12.5.1。大多数方法根本没有效果,可能是我应用不正确。对我有用的是在行视图上放置一个什么都不做的onTapGesture()。也许这会拦截导致行被选中的任何东西。现在对我有用,因为我不想发生任何意外选择,但我可能还没有完全解决问题。 - Bruce Cichowlas
13个回答

74

对于那些和我一样在寻找答案的人们,我知道我来晚了些

我找到的答案

我猜你应该看一下这篇简短文章:如何禁用按钮和导航链接中图像的叠加颜色,作者是@TwoStraws

只需在List中的项目上添加.buttonStyle(PlainButtonStyle())修改器,就可以得到想要的效果。这还可以使List中的Button再次正常工作,这也是我遇到的另一个问题。

Swift 5.1的可行示例:

import Combine
import SwiftUI

struct YourItem: Identifiable {
    let id = UUID()
    let text: String
}

class YourDataSource: ObservableObject {
    let willChange = PassthroughSubject<Void, Never>()
    var items = [YourItem]()

    init() {
        items = [
            YourItem(text: "Some text"),
            YourItem(text: "Some other text")
        ]
    }
}

struct YourItemView: View {
    var item: YourItem

    var body: some View {
        VStack(alignment: .leading) {
            Text(item.text)
            HStack {
                Button(action: {
                    print("Like")
                }) {
                    Image(systemName: "heart.fill")
                }
                Button(action: {
                    print("Star")
                }) {
                    Image(systemName: "star.fill")
                }
            }
        }
        .buttonStyle(PlainButtonStyle())
    }
}

struct YourListView: View {
    @ObservedObject var dataSource = YourDataSource()

    var body: some View {
        List(dataSource.items) { item in
            YourItemView(item: item)
        }
        .navigationBarTitle("List example", displayMode: .inline)
        .edgesIgnoringSafeArea(.bottom)
    }
}

#if DEBUG
struct YourListView_Previews: PreviewProvider {
    static var previews: some View {
        YourListView()
    }
}
#endif

如文章所述,它也适用于NavigationLink。我希望它能帮助你们中的一些人


11
此解决方案在 XCode 11.3.1 上无效,点击列表项时仍会出现选择,我使用的项目是一个 ZStack。 - JAHelia
你确定你直接将修改器应用于列表项(而不是子视图)吗?如果是这样,我会在我的端上尝试并告诉你是否有同样的问题。 - Rémi B.
尝试了两种方法,但是出现了相同的问题,因为我的列表在导航视图中。 - JAHelia
选择仍然会在初始点击时出现,但是通过这个修复,在从 NavigationLink 返回(即“返回”)时它将被取消选择,这正是我主要想要解决的问题。 - Snips

46

简单回答您的问题。任何不希望在点击时突出显示的单元格,只需添加此修改器。

.buttonStyle(PlainButtonStyle())
因此,这个修饰符并不是针对整个列表的,而是针对每个单元格。
var body: some View{
    List{
        ForEach(self.getElementsForList()){ element in
            ElementCell(element: element)
                .buttonStyle(PlainButtonStyle())
        }
    }
}

1
不支持 iOS 15。 - Petar
@Petar 我会看一下的。不过我认为现在可能已经有更新的答案了。 - Julio Bailon
在我的 iOS 15.4 上运行良好。 - FreeNickname
在 macOS 上对我没用 - Peter Lapisu
1
抱歉,无法在iOS 16上运行。 - stromyc
抱歉,在iOS 17上无法工作。 - Lawrence Gimenez

27

.listRowBackground 对我很有帮助


List {
   ListItem()
       .listRowBackground(Color.clear)
}

5
这是正确的答案,只需使用 listRowBackground 即可消除选定的灰色背景。 - Lluis Gerard
2
这是唯一在iOS 16.1上对我有效的答案。我尝试了所有其他解决方案,但都没有用,谢谢!请注意,我正在进行iOS/macOS开发,因此我的实际解决方案如下:#if os(iOS) .listRowBackground(Color.white) #elseif os(macOS) .listRowBackground(Color.clear) #endif - Daniel Walton
1
这是今天的正确答案(iOS16 + iOS15兼容性)。 - valvoline

16

我知道我来晚了,但希望这可以解决你的问题。 您需要使用UIKit修饰符来删除它。我建议您将它们放在SceneDelegate.swift上。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
        let contentView = TabController()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {

            //MARK: Disable selection.
            UITableView.appearance().allowsSelection = false
            UITableViewCell.appearance().selectionStyle = .none

            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()

        }
    }
}

编辑: 这将会禁止你的应用中所有表格视图的选择。如果相反,你想要禁用特定表格视图上的选择,你可以在init()内部禁用它。

struct SomeViewWithTableView: View {

    init() {
        //MARK: Disable selection.
        UITableView.appearance().allowsSelection = false
        UITableViewCell.appearance().selectionStyle = .none
    }

    var body: some View {
        //your view code here
    }
}

1
我可以确认这个有效,我甚至不知道SwiftUI的List只是一个UITableView包装器! - malhal
1
你可以在视图本身上禁用选择。像这样:init() { UITableView.appearance().allowsSelection = false UITableViewCell.appearance().selectionStyle = .none } - Ranic
1
这个有适用于ScrollView的版本吗?它们似乎不响应TableView的设置。 - eResourcesInc
UITableViewCell.appearance().selectionStyle = .none 这个就够了。 - Cinn

6

这需要根据iOS的不同采用不同的方法。

iOS 14:

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

对于iOS 15:

List {
   ListItem()
       .listRowBackground(Color.clear)
}

2
iOS 17
我不得不在我的行中添加.listRowBackground(Color(UIColor.secondarySystemGroupedBackground))。
这样可以保留默认的背景颜色,而不是使其变得透明。

2

只是在List中的项目上添加.buttonStyle(PlainButtonStyle())修饰符,对我的情况并没有起作用。

相反,我通过在List上使用.onAppear()修饰符以及UITableViewCell的Appearance API来获得所需的效果--在点击行时不会看到任何高亮效果。

例如:

List {
    ForEach(items) { item in
        NavigationLink(destination: DetailView(item)) {
            RowView(item)
        }
    }
}
.onAppear {

    // this will disable highlighting the cell when is selected
    UITableViewCell.appearance().selectionStyle = .none

    // you can also remove the row separators
    UITableView.appearance().separatorStyle = .none

}

7
无法在 iOS 15 上运行。 - Mycroft Canner

0

如果我理解正确,您希望通过单击/轻拍来选择项目,并在再次单击时取消选择。

struct SimpleSelectionView: View {
    @State var items = ["First", "Second", "Third"]
    @State var selectedItem: String?
    
    var body: some View{
        List{
            ForEach(items, id: \.self) { item in
                Text(item)
            .onTapGesture {
                if selectedItem == nil || selectedItem != item {
                selectedItem = item
                } else {
                    selectedItem = nil
                        }
                    }
                  .listRowBackground(self.selectedItem == item ? Color(red: 0.275, green: 0.420, blue: 0.153, opacity: 0.3) : .white)
                }
            }
    }
}

缺点是在 macOS 上,您必须点击内容(文本)而不是行本身,但这是一个不错的轻量级解决方案,如果您想对所选内容进行操作,则可以在 didSet 中完成。


0

我遇到了同样的问题,我认为这是一个bug。

如果我在列表上方放置一个文本,导航链接项将会被突出显示;

如果我删除它或将其放置在列表下方,则导航链接项将不会被突出显示。

VStack {
    Text("Some word") // Remove it or put it below List
    List {
        NavigationLink(destination: RedirectionFromList(data: data.first!)) {
            Text("test")
        }
    }
    
}

如果将文本放在列表顶部,我找不到消除高亮的方法,非常烦恼。

最后,我将文本作为一个项目放入列表中。 - pfcstyle
1
在iOS 15中不起作用。 - Mycroft Canner

0
.onDisappear {
    UITableViewCell.appearance().isSelected = false
}

当界面消失或出现时,您可以将选定设置为 false。


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