如何在不使用 ForEach 的情况下从 SwiftUI 中的列表中删除行分隔符?

How to remove the line separators from a List in SwiftUI without using ForEach?

我有显示自定义行列表的代码。

struct ContentView : View {
    var body: some View {
        VStack(alignment: .leading) {
            List(1...10) {_ in
                CustomRow()
            }
        }
    }
}

但是,我想删除每一行上的线。我尝试不使用 List 而是在 ScrollView 中使用 ForEach 但它完全删除了所有样式,包括其填充和边距。我只想删除这些行,没有别的。

请帮忙,谢谢。

iOS 15+:

只需将 .listRowSeparator(.hidden) 作为修饰符添加到包含在 List 中的视图中。 https://developer.apple.com/documentation/swiftui/texteditor/listrowseparator(_:edges:)

List {
    ForEach(garage.cars) { car in
        Text(car.model)
            .listRowSeparator(.hidden)
    }
}

iOS 仅 13:

List 出现之前在代码中的任何位置添加 UITableView.appearance().separatorColor = .clear 应该可以。虽然此解决方案删除了​​分隔符,但请注意所有 List 实例都将绑定到此样式,因为目前没有官方方法只能删除特定实例的分隔符。您可以在 onAppear 中 运行 这段代码并在 onDisappear 中撤消它以保持样式不同。

另请注意,此代码假定 Apple 使用 UITableView 来支持 List,这在 iOS 14 SDK 中并非如此。希望他们将来添加官方 API。感谢 https://twitter.com/singy/status/1169269782400647168.

iOS 15:

今年 Apple 推出了一个新修饰符 .listRowSeparator,可用于设置分隔符的样式。你可以通过 .hidden 来隐藏它:

List {
    ForEach(items, id:\.self) { 
        Text("Row \([=10=])")
            .listRowSeparator(.hidden)
    }
}

iOS 14:

您可以考虑在 ScrollView 中使用 LazyVStack(因为 iOS 支持 SwiftUI 的 UIAppearance不再列出)。


iOS 13:

⚠️ This method is deprecated and it's not working from iOS 14

在 SwiftUI 的 List 后面有一个 UITableView 用于 iOS 13。因此要删除

额外的分隔符(列表下方):

您需要 tableFooterView 并删除

所有分隔符(包括实际分隔符):

你需要 separatorStyle 才能成为 .none

用法示例

init() {
    if #available(iOS 14.0, *) { 
        // iOS 14 doesn't have extra separators below the list by default.
    } else {
        // To remove only extra separators below the list:
        UITableView.appearance().tableFooterView = UIView()
    }

    // To remove all separators including the actual ones:
    UITableView.appearance().separatorStyle = .none
}

var body: some View {
    List {
        Text("Item 1")
        Text("Item 2")
        Text("Item 3")
    }
}

请注意,默认情况下,静态列表不会在列表下方显示额外的分隔符

查看 SwiftUI-Introspect。它公开了底层的 UIKit/AppKit 视图。

iOS 仅 13 个版本:

在这种情况下,您可以直接操作 UITableView(无需通过外观代理更改所有 table 视图):

    import Introspect
    :
    :
    List {
        ...
    }.introspectTableView { tableView in
         tableView.separatorStyle = .none
    }

做类似的事情:

UITableView.appearance().separatorColor = .clear

有效,但在很多情况下我不推荐这样做。这些是全局更改 - 即它们将影响 UITableView 的 all 个实例。如果您有多个需要不同样式的 UITableView,这就是一个问题。或者,如果您正在开发一个框架,那么使用您的框架的客户也会继承这些更改!

更安全的解决方案是仅针对位于指定容器内的 UITableView。幸运的是 appearance api 给了我们一个具体的方法:

UITableView.appearance(whenContainedInInstancesOf: [UIHostingController<YourSwiftUiViewHere>.self]).separatorColor = .clear

使用 ScrollView?

代表您列表的某个州

@State var menuItems: [String] = ["One", "Two", "Three"]

SwiftUI ForEach 循环 ScrollView

ScrollView {
    ForEach(self.menuItems, id: \.self) { item in
        Text(item)
    }
}

iOS 仅 13 个版本:

虽然这些答案在技术上是正确的,但根据我的经验,它们会影响 ListForm 全局(在整个应用程序中)。

我发现至少在我的应用程序中解决此问题的一种 hacky 方法是将以下代码添加到应用程序的“主要”内容视图:

.onAppear(perform: {
    UITableView.appearance().separatorStyle = .none
})

然后在您想要分隔线的任何其他视图上将其添加到 ListForm 视图的末尾

.onAppear(perform: {
    UITableView.appearance().separatorStyle = .singleLine
})

这似乎将单行分隔符添加到主内容视图上方的任何视图 sheet。同样,这都是我最近的 SwiftUI 体验的轶事。

根据我的经验,我只需将 .onAppear(... = .singleLine) 方法添加到我的“详细信息”视图之一,分隔线就会出现在所有随后显示的视图中。

编辑: 另一个注意事项,因为这个答案继续受到关注。我 posted 的这个解决方案并没有解决所有情况,在某些情况下,它当然也没有为我解决。我最终使用 Introspect for SwiftUI 解决了整个应用程序中的这个问题。

我希望这能消除人们遇到此问题时的一些困惑和沮丧 post。

IOS 14

IOS 14 beta 目前没有隐藏分隔符的解决方案。

如果您不需要可编辑的 List,您应该在 ScrollView 中使用 LazyVStack

但是如果你想留在List。我在 samwarner 的 Apple 论坛上找到了解决方案。 https://developer.apple.com/forums/thread/651028

这是一个临时解决方案。在某些情况下,您可能需要调整插图。 这是它的实现:

struct HideRowSeparatorModifier: ViewModifier {
    static let defaultListRowHeight: CGFloat = 44
    var insets: EdgeInsets
    var background: Color
    
    init(insets: EdgeInsets, background: Color) {
        self.insets = insets
        var alpha: CGFloat = 0
        UIColor(background).getWhite(nil, alpha: &alpha)
        assert(alpha == 1, "Setting background to a non-opaque color will result in separators remaining visible.")
        self.background = background
    }
    
    func body(content: Content) -> some View {
        content
            .padding(insets)
            .frame(
                minWidth: 0, maxWidth: .infinity,
                minHeight: Self.defaultListRowHeight,
                alignment: .leading
            )
            .listRowInsets(EdgeInsets())
            .background(background)
    }
}

extension EdgeInsets {
    static let defaultListRowInsets = Self(top: 0, leading: 16, bottom: 0, trailing: 16)
}

extension View {
    func hideRowSeparator(insets: EdgeInsets = .defaultListRowInsets, background: Color = .white) -> some View {
        modifier(HideRowSeparatorModifier(insets: insets, background: background))
    }
}

最后,这里是列表中的实现。您必须在列表单元格中添加 .hideRowSeparator()

struct CustomRow: View {
    let text: String
    
    var body: some View {
        HStack {
            Text(self.text)
            Image(systemName: "star")
        }
    }
}

struct ContentView : View {
    @State private var fruits = ["Apple", "Orange", "Pear", "Lemon"]
    
    var body: some View {
        VStack(alignment: .leading) {
            List {
                ForEach(self.fruits, id: \.self) { str in
                    CustomRow(text: str)
                        .hideRowSeparator()
                }
            }
        }
        .padding(.top)
    }
}

我在 iOS14 开始了一个项目来解决这个问题,因为 iOS 13 变通办法不再有效。它允许设置分隔符样式、分隔符颜色和分隔符插入。

隐藏列表中的分隔符

List { <content> }
    .listSeparatorStyle(.none)

显示带有可配置颜色和插图的单个分隔线

List { <content> }
    .listSeparatorStyle(.singleLine, color: .red, inset: EdgeInsets(top: 0, leading: 50, bottom: 0, trailing: 20)

https://github.com/SchmidtyApps/SwiftUIListSeparator

所有答案都告诉你使用ScrollView(这也是我推荐的)

但如果您想使用 List 并希望删除分隔线..

安装 SwiftPM:https://github.com/siteline/SwiftUI-Introspect

样本:

List {
    Text("Item 1")
    Text("Item 2")
}
.introspectTableView { tableView in
    tableView.separatorStyle = .none
}

这是我的 ListRowExtensions 扩展 hide 列表行分隔符和 custom 这个。

import SwiftUI

// MARK: List row extensions

extension View {
    
    func hideListRowSeparator() -> some View {
        return customListRowSeparator(insets: .init(), insetsColor: .clear)
    }
    
    func customListRowSeparator(
        insets: EdgeInsets,
        insetsColor: Color) -> some View {
        modifier(HideRowSeparatorModifier(insets: insets,
                                          background: insetsColor
        )) .onAppear {
            UITableView.appearance().separatorStyle = .none
            UITableView.appearance().separatorColor = .clear
        }
    }
}

// MARK: ViewModifier

private struct HideRowSeparatorModifier: ViewModifier {
        
    var insets: EdgeInsets
    var background: Color
    
    func body(content: Content) -> some View {
        content
            .padding(insets)
            .frame(
                minWidth: 0,
                maxWidth: .infinity,
                maxHeight: .infinity,
                alignment: .leading
            )
            .listRowInsets(EdgeInsets())
            .background(background)
    }
}

使用:

// Without list row separator
List {
    ForEach(self.viewModel.data, id: \.id) { item in
        Text("Text")
    }
    .hideRowSeparatorItemList()
}

// With list row separator with color and size
List {
    ForEach(self.viewModel.data, id: \.id) { item in
        Text("Text")
    }
    .customListRowSeparator(insets: EdgeInsets(top: 0,
                                               leading: 0,
                                               bottom: 5,
                                               trailing: 0),
                            insetsColor: Color.red)
}

我不确定您是否需要 SwiftUI 中“UITableView”的所有功能,但是如果您只想在 iOS 13 或更高版本中显示视图列表,您不能这样做:

ScrollView {
    VStack(alignment: .leading) {
        ForEach(1...10) { _ in
            CustomRow()
        }
    }
}

然后添加 .padding() 以获得您想要的任何边距?

删除填充和分隔符

iOS14.2,Xcode12.2

ScrollView {
    LazyVStack {
        ForEach(viewModel.portfolios) { portfolio in
            PortfolioRow(item: portfolio)
        }
    }
}

这使您可以完全控制列表。 List 的当前实现不提供完全控制并包含一些问题。

对于 iOS 14 你有这个:

.listStyle(SidebarListStyle()) # IOS 14

您可以通过将 listStyle 设置为 InsetListStyle() 来删除分隔符,如下所示:

.listStyle(InsetListStyle())

将此添加到代码的结尾

这似乎是唯一对我有用的东西。

List() {

}
.listStyle(SidebarListStyle())

适用于 iOS 13 和 14

的简单解决方案
extension List {
func removeSeparator() -> some View {
    if #available(iOS 14.0, *) {
        return self.listStyle(SidebarListStyle()).erasedToAnyView()
    } else {
        return self.onAppear {
            UITableView.appearance().separatorStyle = .none
        }.erasedToAnyView()
    }
}

可以只使用负插图和纯色来遮盖分隔符。

我也遇到了同样的问题。但我知道一个手工制作的解决方案。因此,如果您将列表行参数设置为:

.listRowInsets(EdgeInsets(top: -5, leading: 0, bottom: 0, trailing: 0))

和行视图正文的填充如

.padding(EdgeInsets(top: Statics.adjustValue(v: 10), leading: Statics.adjustValue(v: 10), bottom: Statics.adjustValue(v: 10), trailing: Statics.adjustValue(v: 10)))

然后分隔符将被隐藏。

对于所有 iOS 个版本

对于iOS13,iOS14去掉第一个单元格顶部的分隔符

添加viewModifier

extension View {
    func hideRowSeparator(insets: EdgeInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0),
                          background: Color = .white) -> some View {
        modifier(HideRowSeparatorModifier(insets: insets, background: background))
    }
}

struct HideRowSeparatorModifier: ViewModifier {

  static let defaultListRowHeight: CGFloat = 44

  var insets: EdgeInsets
  var background: Color

  init(insets: EdgeInsets, background: Color) {
    self.insets = insets

    var alpha: CGFloat = 0
    if #available(iOS 14.0, *) {
        UIColor(background).getWhite(nil, alpha: &alpha)
        assert(alpha == 1, "Setting background to a non-opaque color will result in separators remaining visible.")
    }
    self.background = background
  }

  func body(content: Content) -> some View {
    content
        .padding(insets)
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: Self.defaultListRowHeight)
        .listRowInsets(EdgeInsets())
        .overlay(
            VStack {
                HStack {}
                .frame(maxWidth: .infinity)
                .frame(height: 1)
                .background(background)
                Spacer()
                HStack {}
                .frame(maxWidth: .infinity)
                .frame(height: 1)
                .background(background)
            }
            .padding(.top, -1)
        )
  }
}

用法

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0 ..< 30) { item in
                HStack(alignment: .center, spacing: 30) {
                    Text("Hello, world!:\(item)").padding()
                }
                .hideRowSeparator(background: .white)
            }
        }
        .listStyle(PlainListStyle())
    }
}

对于iOS 14:

由于.listRowSeparator(.hidden)仅适用于iOS 15,您可以通过显式将edgeinsets设置为0来隐藏低版本中的分隔符。

内容视图:

List {
    ForEach(viewModel.items, id: \.id) { item in
        YourListItem(item)
        .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
    }
}

伴随上述变化,将行项目的背景设为白色(或根页面的颜色)

行项目:

var body: some View {
    VStack {
        ..... your content
    }
    .background(Colors.white)
}

VStack 只是一个例子。它可以是任何组件。

这是我的解决方案,包含所有注意事项:

    
    let style: UITableViewCell.SeparatorStyle
    
    public func body(content: Content) -> some View {
        content
            .introspectTableView { tableView in
                     tableView.separatorStyle = .none
            }
    }
}
 
public extension View {
    
    func listSeparator(style: UITableViewCell.SeparatorStyle) -> some View {
        ModifiedContent(content: self, modifier: ListSeparatorStyle(style: style))
    }
}

已实施:

List {
   // code...
}
.listSeparator(style: .none)

要删除 SwiftUI 中的行分隔符,您可以通过附加 onAppear 修饰符来调整 List 视图,并调用 UITableView 的外观 API 来禁用行分隔符:

.onAppear {
 UITableView.appearance().separatorStyle = .none
}

使用此代码,应删除列表视图的所有行分隔符。