如何在SwiftUI中修改列表的背景颜色?

134

我正在尝试使用SwiftUI重新创建我之前用UIKit构建的用户界面,但是我遇到了一些小问题。

我想要更改这里 List 的颜色,但是似乎没有任何属性能按照我的期望工作。以下是示例代码:

struct ListView: View {
    @EnvironmentObject var listData: ListData

       var body: some View {
        NavigationView {
            List(listData.items) { item in
                ListItemCell(item: item)
            }
            .content.background(Color.yellow) // not sure what content is defined as here
            .background(Image("paper-3")) // this is the entire screen 
        }
    }
}

struct ListItemCell: View {
    let item: ListItem

    var body: some View {

        NavigationButton(destination: Text(item.name)) {
            Text("\(item.name) ........................................................................................................................................................................................................")
                .background(Color.red) // not the area I'm looking for
        }.background(Color.blue) // also not the area I'm looking for
    }
}

我想改变白色区域


这对我有用。将其添加到我的ContentView_Previews中。当您使用List时,请确保顶部的任何堆栈都在顶部具有spacer。.edgesIgnoringSafeArea(.all) 喜欢调皮捣蛋。 - Alias111
在另一个答案的帮助下,我能够像您的屏幕截图一样以不同的方式为列表中的每个元素着色。https://dev59.com/CVMI5IYBdhLWcg3wUZyd#69514684 - charelf
对于任何未来的观众,这是 iOS 16 版本 https://dev59.com/ClEG5IYBdhLWcg3wI06g#72650158 - Timmy
30个回答

2
对于我而言,在SwiftUI中更改列表背景的完美解决方案是:
struct SomeView: View {
    init(){
    UITableView.appearance().backgroundColor = UIColor(named: "backgroundLight")
      }
...

}

1
使用UITableView.appearance().backgroundColor不是一个好主意,因为它会改变所有表格的背景颜色。我在iOS 14, 15中找到了一个可以在您选择的确切表格中更改颜色的解决方案。
我们将使用一个修饰符来更改颜色,该修饰符需要应用于List内部。
extension View {
    
    func backgroundTableModifier(_ color: UIColor? = nil) -> some View {
        self.modifier(BackgroundTableModifier(color: color))
    }

}

我们的任务是找到UITableView,然后更改颜色。
private struct BackgroundTableModifier: ViewModifier {
    
    private let color: UIColor?
    @State private var tableView: UITableView?
    
    init(color: UIColor?) {
        self.color = color
    }
    
    public func body(content: Content) -> some View {
        if tableView?.backgroundColor != color {
            content
                .overlay(BackgroundTableViewRepresentable(tableBlock: { tableView in
                    tableView.backgroundColor = color
                    self.tableView = tableView
                }))
        } else {
            content
        }
    }
}

private struct BackgroundTableViewRepresentable: UIViewRepresentable {
    
    var tableBlock: (UITableView) -> ()
    
    func makeUIView(context: Context) -> BackgroundTableView  {
        let view = BackgroundTableView(tableBlock: tableBlock)
        return view
    }
    
    func updateUIView(_ uiView: BackgroundTableView, context: Context) {}
}

class BackgroundTableView: UIView {
    
    var tableBlock: (UITableView) -> ()
    
    init(tableBlock: @escaping (UITableView) -> ()) {
        self.tableBlock = tableBlock
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        if let tableView = findTableView(in: self) {
            tableBlock(tableView)
        }
    }
    
    private func findTableView(in view: UIView) -> UITableView? {
        if let tableView = view as? UITableView {
            return tableView
        }
        
        if let superView = view.superview {
            return findTableView(in: superView)
        }
        
        return nil
    }
    
}

为了找到UITableView,修饰符必须在List内部。自然地,您需要确保修饰符仅被调用一次,不需要将其应用于每一行。以下是一个使用示例。
List {
   rows()
     .backgroundTableModifier(.clear)
}

func rows() -> some View {
    ForEach(0..<10, id: \.self) { index in
        Row()
    }
}

谢谢!这个完美地运行并解决了应用程序中所有tableView着色的问题! - Marwen Jamel

1

应用修饰符之前和之后的效果

iOS 16

struct SomeView: View {
    var body: some View {
        ZStack {
            List {
                ForEach(0...5, id: \.self) { i in
                    Text("Sample text")
                } //: LOOP
                .listRowBackground(Color.clear)
                .listRowSeparator(.hidden)
            } //: LIST
            .scrollContentBackground(.hidden)
        } //: ZSTACK
        .background(
            Color.yellow.ignoresSafeArea(.all)
        )
    }
}

0
你可以使用来自Github的introspect库来设置底层表视图的背景颜色,就像这样:
List { ... } .introspectTableView { tableView in
                tableView.backgroundColor = .yellow
            }

0

制作扩展列表,如下:

extension List{
@available(iOS 14, *)
func backgroundList(_ color: Color = .clear) -> some View{
    UITableView.appearance().backgroundColor = UIColor(color)
    return self
}

}


0

如果您想避免全局设置所有表视图的外观,可以将 UITableView.appearance(whenContainedInInstancesOf:)UIHostingController 结合使用。感谢 DanSkeel 在上面留下评论指出这一点。以下是我如何使用它:

public class ClearTableViewHostingController<Content>: UIHostingController<Content> where Content: View {
    public override func viewDidLoad() {
        UITableView.appearance(whenContainedInInstancesOf: [ClearTableViewHostingController<Content>.self]).backgroundColor = .clear
    }
}

您可以像这样使用ClearTableViewHostingController:

let view = MyListView()
let viewController = ClearTableViewHostingController(coder: coder, rootView: view)

然后在你的视图中,你可以这样设置列表的背景颜色:

List {
    Text("Hello World")
}
.background(Color.gray)

1
这是我找到的最佳解决方案,只需稍作更改。我没有使用包装类,而是在视图的init中设置外观。 init() { UITableView.appearance(whenContainedInInstancesOf: [UIHostingController<MyView>.self]).backgroundColor = .color } - Nikolai Prokofev

0

如果有人来到这里寻找在iPhone X / 11上横向背景不全宽的解决方案,请尝试:

.listRowBackground(Color("backgroundColour").edgesIgnoringSafeArea(.all))

0

我受到启发,用于配置每个页面的 NavigationView 导航栏样式,并编写了一些简单的 UITableView 每页配置器,而不使用 UITableView.appearance() 全局方法。

   import SwiftUI

    struct TableViewConfigurator: UIViewControllerRepresentable {

        var configure: (UITableView) -> Void = { _ in }

        func makeUIViewController(context: UIViewControllerRepresentableContext<TableViewConfigurator>) -> UIViewController {

            UIViewController()
        }

        func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<TableViewConfigurator>) {

            let tableViews = uiViewController.navigationController?.topViewController?.view.subviews(ofType: UITableView.self) ?? [UITableView]()

            for tableView in tableViews {
                self.configure(tableView)
            }
        }
    }

然后需要编写UIView扩展来查找所有的UITableView

extension UIView {
    func subviews<T:UIView>(ofType WhatType:T.Type) -> [T] {
        var result = self.subviews.compactMap {$0 as? T}
        for sub in self.subviews {
            result.append(contentsOf: sub.subviews(ofType:WhatType))
        }
        return result
    }
}

最后的使用方法是:

List {

}.background(TableViewConfigurator {
    $0.backgroundColor = .red
})

也许有一件事情应该改进,那就是使用navigationController?.topViewController使其在视图控制器层次结构中没有导航控制器的情况下也能正常工作。

0
import SwiftUI

extension View {
    func blurredSheet<Content: View>(_ style: AnyShapeStyle, isPresented: Binding<Bool>, onDismiss: @escaping ()->(), @ViewBuilder content: @escaping ()->Content) -> some View {
        self
            .sheet(isPresented: isPresented, onDismiss: onDismiss) {
                content()
                    .background(RemoveBackgroundColor())
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background {
                        Rectangle()
                            .fill(style)
                            .ignoresSafeArea(.container, edges: .all)
                    }
            }
    }
}

fileprivate struct RemoveBackgroundColor: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {
        DispatchQueue.main.async {
            uiView.superview?.superview?.backgroundColor = .clear
        }
    }
}

3
感谢您对为 Stack Overflow 社区做出贡献的兴趣。这个问题已经有相当多的答案,其中一个答案已经经过社区的广泛验证。您确定您的方法之前没有被提到过吗?如果是这样,解释一下您的方法有何不同,在什么情况下可能更好,并/或者为什么您认为之前的答案不够满意会很有帮助。能否请您编辑您的回答并提供解释? - Jeremy Caney

-5

Xcode版本12.4

Background属性对我起作用了,但必须使用不透明度才能生效。 如果没有不透明度,它将无法起作用。

List {
            ForEach(data, id: \.id) { (item) in
                ListRow(item)
                    .environmentObject(self.data)
            }
        }
        .background(Color.black)
        .opacity(0.5)

虽然这样做可以起作用,但它也会使列表变得透明。我尝试了opacity(0.5)opacity(1)opacity(0),但没有一个结果是列表行存在且列表背景颜色符合要求的。 - charelf
这是一个无关紧要的答案,而且也不会起作用。 - Ali Pishvaee

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