简体   繁体   English

如何修改SwiftUI中List的背景色?

[英]How do I modify the background color of a List in SwiftUI?

I'm trying to recreate an UI I built with UIKit in SwiftUI but I'm running into some minor issues.我正在尝试重新创建我在 SwiftUI 中使用 UIKit 构建的 UI,但我遇到了一些小问题。

I want the change the color of the List here, but no property seems to work as I expects.我想在这里更改List的颜色,但似乎没有任何属性像我预期的那样工作。 Sample code below:示例代码如下:

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
    }
}

我想改变白色区域

Ok, I found the solution for coloring the list rows:好的,我找到了为列表行着色的解决方案:

struct TestRow: View {

    var body: some View {
        Text("This is a row!")
        .listRowBackground(Color.green)
    }
}

and then in body:然后在正文中:

List {
    TestRow()
    TestRow()
    TestRow()
}

This works as I expect, but I have yet to find out how to then remove the dividing lines between the rows...这可以按我的预期工作,但我还没有弄清楚如何删除行之间的分隔线......

This will set the background of the whole list to green:这会将整个列表的背景设置为绿色:

init() {
   UITableView.appearance().separatorStyle = .none
   UITableViewCell.appearance().backgroundColor = .green
   UITableView.appearance().backgroundColor = .green
}

在此处输入图像描述

struct ContentView: View {

    var strings = ["a", "b"]

    var body: some View {

        List {
            ForEach(strings, id: \.self) { string in
                Text(string)
            }.listRowBackground(Color.green)
        }
    }
}

You can do it by changing UITableView 's appearance.您可以通过更改UITableView的外观来做到这一点。

UITableView.appearance().backgroundColor = UIColor.clear

just put this line in Appdelegate 's didFinishLaunchingWithOptions method.只需将此行放在AppdelegatedidFinishLaunchingWithOptions方法中。 In replace of UIColor.clear set whatever color you want to add in background color of list .代替UIColor.clear设置要在list的背景颜色中添加的任何颜色。

Changing Background Color更改背景颜色

As other have mentioned, changing the UITableView background will affect all other lists in your app.正如其他人所提到的,更改 UITableView 背景将影响您应用中的所有其他列表。

However if you want different background colors you can set the default to clear , and set the background color in swiftui views like so:但是,如果您想要不同的背景颜色,您可以将默认设置为clear ,并在 swiftui 视图中设置背景颜色,如下所示:

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
// Ignore safe area to take up whole screen
.background(Color.purple.ignoresSafeArea())
.onAppear {
    // Set the default to clear
    UITableView.appearance().backgroundColor = .clear
}

You probably want to set the tableview appearance earlier, such as in the SceneDelegate or root view like so:您可能希望更早地设置 tableview 外观,例如在SceneDelegate或根视图中,如下所示:

// SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
      
    
    guard let windowScene = scene as? UIWindowScene else {
        print("Returning because screne does not exist")
        return
            
    }
    
    // Set here    
    UITableView.appearance().backgroundColor = .clear
    let contentView = ContentView()
    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = UIHostingController(rootView: contentView)
    self.window = window
    window.makeKeyAndVisible()
}


// Root App View
@main
struct ListBackgroundApp: App {
    
    init() {
        UITableView.appearance().backgroundColor = .clear
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

工作背景颜色变化

There is an argument: listRowBackground() in SwiftUI, but if you use List directly to iterate the data collection, it doesn't work. SwiftUI中有一个说法:listRowBackground(),但是如果你直接用List来迭代数据集合,是行不通的。

Here is my workaround:这是我的解决方法:

    List {
        // To make the background transparent, we have we use a ForEach as a wrapper
        ForEach(files) {file in
            Label(
                title: { Text(file.name ?? fileOptionalFiller).lineLimit(listRowTextLineLimit) },
                icon: { AppIcon.doc.foregroundColor(.primary) }
            )
        }
        .listRowBackground(Color.primary.colorInvert())
    }

Basically, listRowBackground() works if you use a ForEach inside List.基本上,如果您在 List 中使用 ForEach,则 listRowBackground() 可以工作。

I was able to get the whole list to change color by using colorMultiply(Color:).通过使用 colorMultiply(Color:),我能够让整个列表更改颜色。 Just add this modifier to the end of the list view, and then the padding will push the table to the device edges.只需将此修饰符添加到列表视图的末尾,然后填充会将表格推到设备边缘。 For example:例如:

List {...}.colorMultiply(Color.green).padding(.top)

https://www.hackingwithswift.com/quick-start/swiftui/how-to-adjust-views-by-tinting-and-desaturating-and-more https://www.hackingwithswift.com/quick-start/swiftui/how-to-adjust-views-by-tinting-and-desaturating-and-more

I do not know what is the connection but if you wrap the list with Form it is working.我不知道有什么联系,但如果你用Form包装列表,它就可以工作。

Form {
     List(viewModel.currencyList, id: \.self) { currency in
        ItemView(item: currency)
     }
      .listRowBackground(Color("Primary"))
      .background(Color("Primary"))
}

2022, Solution for MacOS 2022,MacOS 解决方案

the following code makes ALL OF List s background color transparent:以下代码使所有List的背景颜色透明:

// Removes background from List in SwiftUI
extension NSTableView {
    open override func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        
        backgroundColor = NSColor.clear
        if let esv = enclosingScrollView {
            esv.drawsBackground = false
        }
    }
}

在此处输入图像描述

.......... …………

.......... …………

.......... …………

the following code makes ALL OF TextEditor s background color transparent:以下代码使所有TextEditor的背景颜色透明:

extension NSTextView {
    open override var frame: CGRect {
        didSet {
            backgroundColor = .clear
            drawsBackground = true
        }
    }
}
struct Details: View {

    var body: some View {
        Spacer().overlay(
            List {
                Text("Hello World!").font(.title2)
                    .listRowBackground(Color.clear)
                Text("Hello World again").font(.title2)
                    .listRowBackground(Color.clear)
            }.onAppear() {
                UITableView.appearance().backgroundColor = UIColor.green
                UITableViewCell.appearance().backgroundColor = UIColor.green
            }
        )
    }
}

Someone may find this useful if attempting to create a floating type cell with SwiftUI using .listRowBackground and applying .padding如果尝试使用.listRowBackground并应用.padding使用 SwiftUI 创建浮动类型单元格,有人可能会发现这很有用

var body: some View {
    NavigationView {
        List {
            ForEach (site) { item in
                HStack {
                    Text(String(item.id))

                    VStack(alignment: .leading) {
                        Text(item.name)
                        Text(item.crop[0])
                    }

                }.listRowBackground(Color.yellow)
                      .padding(.trailing, 5)
                      .padding(.leading, 5)
                      .padding(.top, 2)
                      .padding(.bottom, 2))
            }
        }
            .navigationBarTitle(Text("Locations"))
    }
}

The answer by Islom Alimov https://stackoverflow.com/a/59970379/9439097 seems to be the best implementation so far in my opinion. Islom Alimov https://stackoverflow.com/a/59970379/9439097的答案在我看来似乎是迄今为止最好的实现。

Only drawback: this also changes the background color of all other list views in your app, so you need to manually change them back unless you want the same color everywhere.唯一的缺点:这也会更改应用程序中所有其他列表视图的背景颜色,因此您需要手动将它们更改回来,除非您希望到处都使用相同的颜色。

Here is an example view:这是一个示例视图:

import SwiftUI

struct TestView1: View {
    
    init(){
        UITableView.appearance().backgroundColor = UIColor(Color.clear)
    }
    
    @State var data = ["abc", "def"]
    
    var body: some View {
        VStack {
            List {
                ForEach(data, id: \.self) {element in
                    Text("\(String(describing: element))")
                }
                .background(Color.green)
                .listRowBackground(Color.blue)
                
            }
            .background(Color.yellow)
            Spacer()
            Color.red
        }
    }
}

struct TestView1_Previews: PreviewProvider {
    static var previews: some View {
        TestView1()
    }
}

produces:产生:

I assume the listRowPlatterColor modifier should do this, but isn't as of Xcode 11 Beta 11M336w我认为listRowPlatterColor修饰符应该这样做,但不是 Xcode 11 Beta 11M336w

var body: some View {
    List(pokemon) { pokemon in
        PokemonCell(pokemon: pokemon)
            .listRowPlatterColor(.green)
    }
}

For me, a perfect solution to change the background of List in SwiftUI is:对我来说,在 SwiftUI 中更改 List 背景的完美解决方案是:

struct SomeView: View {
    init(){
    UITableView.appearance().backgroundColor = UIColor(named: "backgroundLight")
      }
...

}

List is not perfect yet.列表还不完善。

An option would be to use it like this -> List { ForEach(elements) { }} instead of List($elements)一个选项是像这样使用它 -> List { ForEach(elements) { }}而不是List($elements)

On my end this is what worked best up to now.就我而言,这是迄今为止效果最好的。 Like @FontFamily said, it shouldn't break any List default behaviors like swiping.就像@FontFamily 所说,它不应该破坏任何List默认行为,如滑动。

.colorMultiply(...) .colorMultiply(...)

As an option you can .colorMultiply(Color.yourColor) modifier.作为一个选项,您可以.colorMultiply(Color.yourColor)修饰符。

Warning : this does not change the color!警告这不会改变颜色! This only applies the Multiply modifier to the current color.这只Multiply修改器应用于当前颜色。 Please read the question before any action, because you are probably looking for: "How to CHANGE the background color of a List in SwiftUI" and this will not work for you.请在采取任何行动之前阅读问题,因为您可能正在寻找:“如何在 SwiftUI 中更改列表的背景颜色”,这对您不起作用。 ❄️ ❄️

Example:例子:

List (elements, id:\.self ) { element in

     Text(element)

}
.colorMultiply(Color.red) <--------- replace with your color

在此处输入图像描述

Simply Add UITableView appearance background color in init() method and add list style (.listStyle(SidebarListStyle()) . Don't forget to import UIKit module只需在init()方法中添加UITableView外观背景颜色并添加列表样式(.listStyle(SidebarListStyle()) 。不要忘记导入UIKit模块

struct HomeScreen: View {
init() {
    UITableView.appearance().backgroundColor = .clear
}

let tempData:[TempData] = [TempData( name: "abc"),
                         TempData( name: "abc"),
                         TempData( name: "abc"),
                         TempData( name: "abc")]

var body: some View {
    ZStack {
        Image("loginBackgound")
            .resizable()
            .scaledToFill()
        List{
            ForEach(tempData){ data in
                Text(data.name)
            }
        }
        .listStyle(SidebarListStyle())
        
    }
    .ignoresSafeArea(edges: .all)
}
}

If you want to avoid setting the appearance for all table views globally, you can combine UITableView.appearance(whenContainedInInstancesOf:) with UIHostingController .如果要避免全局设置所有表视图的外观,可以将UITableView.appearance(whenContainedInInstancesOf:)UIHostingController结合使用。 Thanks DanSkeel for the comment you left above pointing this out.感谢DanSkeel您在上面留下的评论指出了这一点。 This is how I used it:这就是我使用它的方式:

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

You can use ClearTableViewHostingController like this:您可以像这样使用ClearTableViewHostingController

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

Then in your view you can set the list background color like so:然后在您的视图中,您可以像这样设置列表背景颜色:

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

Make extension List like:使扩展列表如下:

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

} }

Using UITableView.appearance().backgroundColor is not a good idea as it changes the backgroundColor of all tables.使用UITableView.appearance().backgroundColor不是一个好主意,因为它会更改所有表格的背景颜色。 I found a working solution for color changing at the exact table you selected in iOS 14, 15 .我在您在iOS 14, 15中选择的确切表格上找到了一个可行的颜色更改解决方案。

We will change the color using a modifier that needs to be applied inside the List我们将使用需要在列表中应用的修饰符来更改颜色

extension View {
    
    func backgroundTableModifier(_ color: UIColor? = nil) -> some View {
        self.modifier(BackgroundTableModifier(color: color))
    }

}

Our task is to find the UITableView and after that change the 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
    }
    
}

In order to find UITableView, the modifier must be inside the List.为了找到 UITableView,修饰符必须在 List 内。 Naturally, you need to ensure that the modifier is called only once, you do not need to apply it to each row.自然,您需要确保只调用一次修饰符,而不需要将其应用于每一行。 Here is an example of usage这是一个使用示例

List {
   rows()
     .backgroundTableModifier(.clear)
}

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

I've inspired some of the configurator used to config per page NavigationView nav bar style and write some simple UITableView per page configurator not use UITableView.appearance() global approach我启发了一些用于配置每页 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)
            }
        }
    }

Then there is UIView extension needed to find all UITableViews然后需要 UIView 扩展来查找所有 UITableViews

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
    }
}

And usage at the end is:最后的用法是:

List {

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

Maybe one thing should be improved that is usage of navigationController?.topViewController to make it work even without navigationController in view controllers hierarchy也许应该改进一件事,即使用 navigationController?.topViewController 使其即使在视图控制器层次结构中没有 navigationController 也能工作

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

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

you can use introspect library from Github to set the background color for the underlying table view like this:您可以使用 Github 中的introspect库为基础表视图设置背景颜色,如下所示:

List { ... } .introspectTableView { tableView in
                tableView.backgroundColor = .yellow
            }

For some reason color change is not working, you can try the .listStyle to .plain由于某种原因颜色更改不起作用,您可以尝试将 .listStyle 更改为 .plain

Code:代码:

struct ContentView: View {
var body: some View {
    VStack {
        Text("Test")

        List {
            ForEach(1 ..< 4) { items in
                Text(String(items))
            }
        }
        .listStyle(.plain)
    }
}

iOS 16 provides a modifier to customize List background color. iOS 16 提供了一个修改器来自定义 List 背景颜色。

View.scrollContentBackground specifies the background style of a scrollable view's content, including List. View.scrollContentBackground指定可滚动视图内容的背景样式,包括列表。

List {
    Text("One")
    Text("Two")
}
.scrollContentBackground(Color.red)

You can hide the background via .hidden or Color.clear - if you do that the List's background will be visible.您可以通过.hiddenColor.clear隐藏背景 - 如果这样做,列表的背景将可见。

List {
    Text("One")
    Text("Two")
}
.background(Image("MyImage"))
.scrollContentBackground(.hidden)

You may also want to customize the background of list rows - the individual cells - and separators.您可能还想自定义列表行的背景 - 单个单元格 - 和分隔符。 This can be done like so:这可以这样做:

List {
    Section("Header") {
        Text("One")
        Text("Two")
            .listRowBackground(Color.red)
    }
    .listRowBackground(Color.clear)
    .listRowSeparator(.hidden)
}
.scrollContentBackground(.hidden)

In iOS 16 , we got a native way to do this via scrollcontentbackground modifier.在 iOS 16中,我们通过scrollcontentbackground修饰符获得了一种本地方式来执行此操作。

You can either change the color by setting a color ( ShapeStyle ) to scrollcontentbackground .您可以通过将颜色 ( ShapeStyle ) 设置为scrollcontentbackground更改颜色

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
.scrollContentBackground(Color.pink)

Or you can hide the background .scrollContentBackground(.hidden) and set a custom one with .backgroud modifier.或者您可以隐藏背景.scrollContentBackground(.hidden)并使用.backgroud修饰符设置自定义背景。

List {
    Text("Item 1")
    Text("Item 2")
    Text("Item 3")
}
.background {
    Image("ventura")

}
.scrollContentBackground(.hidden)

Changing background did not work for me, because of the system background.由于系统背景,更改背景对我不起作用。 I needed to hide it.我需要隐藏它。

List(examples) { example in
        ExampleRow(example: example)
    }.background(Color.white.edgesIgnoringSafeArea(.all))
        .scrollContentBackground(.hidden)

Xcode Version 12.4 Xcode 版本 12.4

The Background property worked for me, but with the mandatory use of Opacity . Background属性对我有用,但强制使用Opacity Without opacity it is not work.没有不透明度是行不通的。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM