如何在SwiftUI中移除列表分隔线

32
所以问题很简单,就在标题上。我想在SwiftUI iOS 14中删除行分隔符。以前,我使用的是 UITableView().appearance().separatorStyle = .none 在iOS 13中这样做是有效的。然而,现在它不起作用了。有没有更新或者有什么办法可以让它起作用呢?谢谢 :)

4
好的,现在没有UITableView了...也没有钩子)) - Asperi
3
@SchmidtyApps发布了一个我已确认有效的解决方案!但是答案被管理员删除了,我无法恢复它。请参见https://github.com/SchmidtyApps/SwiftUIListSeparator - Jordan H
2
@Asperi,你的意思是UITableView已经不存在了吗?其实还是有的。 - Petar
12个回答

47

这是一个可能解决方案的演示。使用Xcode 12b测试过。

演示

List {
    ForEach(0..<3) { _ in
        VStack {
            Text("Hello, World!").padding(.leading)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
        .listRowInsets(EdgeInsets())
        .background(Color(UIColor.systemBackground)))
    }
}

4
要使用Color(UIColor.systemBackground)代替Color.white吗? - akmin
3
此外,在某些情况下,您可能希望应用负的边缘插图。.listRowInsets(EdgeInsets(.init(top: -1, leading: -1, bottom: -1, trailing: -1))) - zrfrank
2
你如何在不修改插图的情况下实现这一点?移除所有填充会使列表看起来很糟糕。 - Jordan H
我在我的列表单元格中使用了按钮。添加后,按钮失去了高亮颜色。 - Tulon
如果您确实需要使用非零值的insets来使用.listRowInsets,该怎么办? - Petar
显示剩余2条评论

10

将@asperi、@akmin和@zrfrank的答案合并为一件事。 根据我的经验,ListLazyVStack更可靠和高效,因此,我仍然使用List来处理任何需要可靠性的复杂情况。

extension View {
    func listRow() -> some View {
        self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
            .listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1))
            .background(Color(.systemBackground))
    }
}

List {
    Color.red
         .listRow()


    Color.green
         .listRow()
}

如果你想要有列表分区,LazyVStack 就不太适合了。 - Yiming Dong
这对我有效。列表 + 一些视图(不是LazyVStack) - Brownsoo Han
在iOS 14上,它运行得非常好!没有LazyVStack,只有List + view。谢谢你!我快疯了! - Posa
我感到震惊的是,这在iOS 14上可以运行,但在iOS 15上似乎毫无作用,但事情就是这样。 - Robert Atkins

8

iOS 15:

苹果今年推出了一个新的修饰符.listRowSeparator,可用于样式化分隔线。您可以传递.hidden来隐藏它:

List(items, id:\.self) {
    Text("Row \($0)")
        .listRowSeparator(.hidden)
}

同时,您可以通过设置 listRowSeparatorTintColor 来将每个分隔符设置为任何颜色,如我在这个答案中所提到的:

演示

iOS 14

请参考这里的答案


因为这并没有回答如何在iOS 14上使用SwiftUI 2.0实现它的问题,所以被踩了。 - Alec.

8

我如何创建一个适用于iOS 14和iOS 13的列表,它不显示分隔线和多余的边距。

struct NoButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
    }
}

struct ListWithoutSepatorsAndMargins<Content: View>: View {
        let content: () -> Content
    
        var body: some View {
            if #available(iOS 14.0, *) {
                ScrollView {
                    LazyVStack(spacing: 0) {
                        self.content()
                    }
                    .buttonStyle(NoButtonStyle())
                }
            } else {
                List {
                    self.content()
                }
                .listStyle(PlainListStyle())
                .buttonStyle(NoButtonStyle())
            }
        }
    }

示例用法 -

ListWithoutSepatorsAndMargins {
    ForEach(0..<5) { _ in
      Text("Content")
    }
}

如果列表中有更多的组件,请将它们包装在 Group 中。

                ListWithoutSepatorsAndMargins {
                    Group {
                            self.groupSearchResults()
                            self.myGroups()
                            self.exploreGroups()
                        }
                    }
                }

    

希望这篇文章能够帮到有需要的人,我在一个小问题上浪费了很多时间。苹果似乎在极力推动我们使用LazyVStack。


2
该解决方案是有害的,因为LazyVStack与List完全以不同的方式处理单元格。您可以检查滚动这些单元格时的CPU负载。如果单元格不仅仅是Text("hello test"),则会在滚动时看到峰值负载。 - Vivienne Fosh
滚动时CPU使用率会稍微增加,但随着事物的稳定,它会立即回落,这在UITableView中也会发生。我的应用程序中有相当重的单元格和非常大的列表,但它从未导致滚动卡顿或崩溃,我们迄今为止没有看到任何问题,但请自行决定是否使用! - Harish saini
@Lonkly,你为什么认为这可能是个问题? - Zapko
2
@Zapko,你认为UIKit中StackView和TableView的主要区别是什么?与此相同的是List和LazyVStack。TableView会重用单元格,而LazyVStack则不会。因此,当您拥有更多的元素适合屏幕,并且布局比Text(“Content”)更复杂时,您将遇到极慢和延迟。因此,提供名为ListWithoutSeparators {}的解决方案并将其作为LazyVStack内部是非常有害的。 - Vivienne Fosh
2
使用lazy vstack,您将失去其他List的好处,例如编辑状态、滑动删除手势等。 - Shengchalover
我已经使用List解决了这个问题,如果你想要List,请查看这里https://dev59.com/gFIG5IYBdhLWcg3w21eA#64028051。 - Harish saini

5

我在苹果开发者论坛上找到了这个简单的解决方案。它适用于14.4版本,对我有效:

List {
   ...
}.listStyle(SidebarListStyle())

这似乎在边缘周围添加了一点填充。如果对您来说是个问题,您可以尝试一些负填充。


我认为这是目前最好的解决方案,非常优雅! - phuongzzz

3

根据Average Joe答案,我最终得到了以下修改器:

struct ListSeparatorNone: ViewModifier {

    var backgroundColor: Color = Color(.systemBackground)
    
    func body(content: Content) -> some View {
        content
            .listRowInsets(EdgeInsets(top: -1, leading: 0, bottom: 0, trailing: 0))
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
            .background(backgroundColor)
    }
}

视图扩展:
extension View {
    func listSeparatorNone(backgroundColor: Color = Color(.systemBackground)) -> some View {
        self.modifier(ListSeparatorNone(backgroundColor: backgroundColor))
    }
}

使用示例:

List {
    ForEach(viewModel.countries, id: \.self) { country in
        Text(country)
            .padding(.leading, 10)
    }
    .listSeparatorNone()
}

有时候有效,有时候无效。 - Michał Ziobro

0

这是我针对iOS 14的解决方案:

struct MyRowView: View {
  var body: some View {
    ZStack(alignment: .leading) {
      // Background color of the Row. It will spread under the entire row.
      Color(.systemBackground)
      NavigationLink(destination: Text("Details")) {
        EmptyView()
      }
      .opacity(0) // Hide the Disclosure Indicator

      Text("Go to Details").padding(.leading)
    }
    // These 2 lines hide the row separators
    .padding(.horizontal, -16) // Removes default horizontal padding
    .padding(.vertical, -6)    // Removes default vertical padding
  }
}

封装的列表应该有这个修饰符

.listStyle(PlainListStyle())

与使用 LazyVStack 相比,这种解决方案的优点是您仍然可以使用 List 的编辑功能。

此解决方案不幸依赖于硬编码的值,以删除每行的系统默认填充。希望 SwiftUI 3.0 提供简单的 .separatorStyle(.none) 和 .accessoryType(.none) 修饰符。

去除披露指示器的代码来自:https://www.appcoda.com/hide-disclosure-indicator-swiftui-list/


0
感谢 @asperi、@akmin、@zrfrank 和 @averageJoe 的回答。
这里是另一种在 iOS 14 和 15 中有效的改进方法。
extension View {
    func hideListRowSeperator() -> some View {
        if #available(iOS 15, *) {
            return AnyView(self.listRowSeparator(.hidden))
        } else {
            return AnyView(self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
                .listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1))
                .background(Color(.systemBackground)))
        }
    }
}

使用示例

var body: some View {
    List {
        ForEach(0..<3) { _ in
            Text("Hello, World!")
                .padding(.leading)
                .hideListRowSeperator()
        }
    }
    .listStyle(.plain)
}

0

更新:

我找到了一个解决方案,可以在iOS 13和iOS 14上运行,并提供一个简单的列表,并在iOS上使用List。

struct ListWithoutSepatorsAndMargins<Content>: View where Content: View {
    let content: () -> Content

    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }

    var body: some View {
        List {
            self.content()
                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                .listRowInsets(EdgeInsets())
                .background(Color.white)
        }
        .listStyle(PlainListStyle())
        .buttonStyle(NoButtonStyle())
    }
}

struct NoButtonStyle: ButtonStyle {
    func makeBody(configuration: Self.Configuration) -> some View {
        configuration.label
    }

在 SceneDelegate.swift 中执行以下操作,以删除单元格的默认灰色选择:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    UITableView.appearance().separatorStyle = .none
    UITableView.appearance().allowsSelection = false
   .......

我们可以这样使用它

ListWithoutSepatorsAndMargins {
    ForEach(0..<5) { _ in
      Text("Content")
    }
}

ListWithoutSepatorsAndMargins {
     Group {
        self.groupSearchResults()
        self.myGroups()
        self.exploreGroups()
     }
  }
}

那我们应该在哪里找到SceneDelegate - Ovi Trif
1
在你的应用程序启动时添加第一段被执行的代码。 - Harish saini
在较新版本的iOS中,我发现单元格之间有默认的间距,可以通过以下方法去除。 - Harish saini
`List { self.content() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .listRowInsets(EdgeInsets()) .background(Color.white) } .listStyle(PlainListStyle()) .buttonStyle(NoButtonStyle()) .environment(.defaultMinListRowHeight, 1)` - Harish saini
从之前的解决方案中删除额外的.environment(\.defaultMinListRowHeight, 1) - Harish saini

0

如果您的单元格数量不多,因此不需要依赖LazyVStack来提高性能,那么您可以使用ScrollView + VStack:

ScrollView {
  VStack {
    Row1()
    Row2()
    Row3()
  }
}

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