How can I change the colour of separator in list of SwiftUI? - swift

I created a list in SwiftUI. I want to change the color or remove the separator as, In UIKit, we can easily change the color of separator in TableView.
Below is the code and UI(image) of the list in SwiftUI
#State private var users = ["Paul", "Taylor", "Adele"]
var body: some View {
List(){
ForEach(users, id: \.self) { user in
HStack{
Image("helmet").resizable().frame(width: 40, height: 40, alignment: .leading)
VStack(alignment : .leading){
Text(user).font(.custom("SFProText-Semibold", size: 14))
Text("Blu Connect").font(.custom("SFProText-Semibold.ttf", size: 11))
}
}
}
.onDelete(perform: delete)
}
}

Try this
var body: some View {
UITableView.appearance().separatorColor = UIColor.blue
return List(){
ForEach(users, id: \.self) { user in
HStack{
Image("helmet").resizable().frame(width: 40, height: 40, alignment: .leading)
VStack(alignment : .leading){
Text(user).font(.custom("SFProText-Semibold", size: 14))
Text("Blu Connect").font(.custom("SFProText-Semibold.ttf", size: 11))
}
}
}
.onDelete(perform: delete)
}
}
This is a kind of global variable, so you are changing the separator colour in all UITableViews from your app (List and Form are using UITableViews under the hood)

2021 – Xcode 13 / SwiftUI 3
Change color of separator:
.listRowSeparatorTint(Color.pink)
Hide separator:
.listRowSeparator(.hidden)

iOS 15
🌈 From this year, you can apply color to any separator individually with listRowSeparatorTintColor modifier like:
To remove
From iOS 15, you can use listRowSeparator modifier by passing hidden.
Please read this detailed answer for more information and samples.

If you need to do this on older versions of iOS checkout Introspect
You can then do something like the following to change the separator color to red:
List {
// ...
}
.introspectTableView { tableView in
tableView.separatorColor = UIColor.red
}
for new versions see #MojtabaHosseini's answer

You can also swap out the List() element for a ScrollView() element.

Related

Remove separator line from list iOS 14 [duplicate]

I'm trying to remove the "row" separators (known as dividers in SwiftUI) from a List in SwiftUI.
I went through the List documentation, but I haven't been able to find a modifier for that.
Any help would be appreciated.
iOS 15:
This year Apple introduced a new modifier .listRowSeparator that can be used to style the separators. you can pass .hidden to hide it:
List {
ForEach(items, id:\.self) {
Text("Row \($0)")
.listRowSeparator(.hidden)
}
}
iOS 14
Apple introduced LazyVStack In iOS 14. you may consider using it instead of list for this:
ScrollView {
LazyVStack {
ForEach((1...100), id: \.self) {
Text("Placeholder \($0)")
}
}
}
Keep in mind that LazyVStack is lazy and doesn't render all rows all the time. So they are very performant and suggested by Apple itself in WWDC 2020.
iOS 13
There is a UITableView behind SwiftUI's List for iOS. So to remove
Extra separators (below the list):
you need a tableFooterView and to remove
All separators (including the actual ones):
you need separatorStyle to be .none
init() {
// 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")
}
}
iOS 13 builds only
while this solution works correctly let's clean up the work using
ViewModifier
public struct ListSeparatorStyleNoneModifier: ViewModifier {
public func body(content: Content) -> some View {
content.onAppear {
UITableView.appearance().separatorStyle = .none
}.onDisappear {
UITableView.appearance().separatorStyle = .singleLine
}
}
}
now let's make a small extension that would help to hide the details
extension View {
public func listSeparatorStyleNone() -> some View {
modifier(ListSeparatorStyleNoneModifier())
}
}
As you can see, we’ve wrapped our appearance setting code into a neat little view modifier.
you can declare it directly now
List {
Text("1")
Text("2")
Text("3")
}.listSeparatorStyleNone()
You may use ForEach within a ScrollView instead of List for dynamic views without any styling
iOS 13 builds only:
The current workaround is to remove them via UIAppearance:
UITableView.appearance(whenContainedInInstancesOf:
[UIHostingController<ContentView>.self]
).separatorStyle = .none
iOS 13 builds only:
Adding UITableView.appearance().separatorColor = .clear for initializer
struct SomeView: View {
init() {
UITableView.appearance().separatorColor = .clear
}
}
I hope you to resolve this problem.
iOS 13 builds only:
See existing answers using UITableView.appearance().
⚠️ Be aware that in the iOS 14 SDK, List does not appear to be backed by UITableView. See the alternate solution below:
iOS 14 Xcode 12 Beta 1 only:
I do have a pure SwiftUI solution for iOS 14, but who knows how long it's going to continue working for. It relies on your content being the same size (or larger) than the default list row and having an opaque background.
⚠️ This does not work for iOS 13 builds.
Tested in Xcode 12 beta 1:
yourRowContent
.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
.frame(
minWidth: 0, maxWidth: .infinity,
minHeight: 44,
alignment: .leading
)
.listRowInsets(EdgeInsets())
.background(Color.white)
Or if you're looking for a reusable ViewModifier:
import SwiftUI
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
))
}
}
struct HideRowSeparator_Previews: PreviewProvider {
static var previews: some View {
List {
ForEach(0..<10) { _ in
Text("Text")
.hideRowSeparator()
}
}
.previewLayout(.sizeThatFits)
}
}
From: Swiftui Views Mastery Book SwiftUI 2.0 Mark Moeykens
.listStyle(SidebarListStyle()) # IOS 14
You can apply this new list style which will remove the separator lines.
For iOS13,iOS14,iOS15,and remove the separator at the top of the first cell
Add viewModifier
extension View {
/// 隐藏 List 中的 分割线
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)
)
}
}
Usage
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())
}
}
You can find here
https://github.com/wangrui460/HiddenListLine4SwiftUI
iOS / SwiftUI 技术交流
我创建了一个 微信 iOS 技术交流群、SwiftUI 技术交流群,欢迎小伙伴们加入一起交流学习~
可以加我微信我拉你进去(备注iOS),我的微信号 wr1204607318

SwiftUI: List, NavigationLink, and badges

I'm working on my first SwiftUI app, and in it would like to display a List of categories, with a badge indicating the number of items in that category. The title of the category would be on the left, and the badge would be right-aligned on the row. The list would consist of NavigationLinks so that tapping on one would drill further down into the view hierarchy. The code I've written to render the NavigationLinks looks like this:
List {
ForEach(myItems.indices) { categoryIndex in
let category = categories[categoryIndex]
let title = category.title
let fetchReq = FetchRequest<MyEntity>(entity: MyEntity(),
animation: .default)
NavigationLink(destination: MyItemView()) {
HStack(alignment: .center) {
Text(title)
Spacer()
ZStack {
Circle()
.foregroundColor(.gray)
Text("\(myItemsDict[category]?.count ?? 0)")
.foregroundColor(.white)
.font(Font.system(size: 12))
}
}
}
}
}
While it does render a functional NavigationLink, the badge is not displayed right-aligned, as I had hoped. Instead, it looks like this:
I know I'm getting hung up on something in my HStack, but am not sure what. How do I get it so that the category title Text takes up the majority of the row, with the badge right-aligned in the row?
SwiftUI doesn't know how big your Circle should be, so the Spacer doesn't do anything. You should set a fixed frame for it.
struct ContentView: View {
var body: some View {
List {
ForEach(0..<2) { categoryIndex in
let title = "Logins"
NavigationLink(destination: Text("Hi")) {
HStack(alignment: .center) {
Text(title)
Spacer()
ZStack {
Circle()
.foregroundColor(.gray)
.frame(width: 25, height: 25) // here!
Text("5")
.foregroundColor(.white)
.font(Font.system(size: 12))
}
}
}
}
}
}
}
Result:

How to prevent TextEditor from scrolling in SwiftUI?

I am trying to create a list of TextEditors as part of a SwiftUI app. I want the individual editors not to scroll, as they make up a larger scrolling list. However, when I add the TextEditors to the list, they compress down and each view becomes individually scrollable. Is there a modifier / trick to getting the TextEditor to always fit its text content without scrolling, so I can achieve this?
A minimal example of what I'm trying to do is below:
struct ParentView: View {
var items: [Item]
var body: some View {
List(items, id: \.id) { item in
EditorView(item: item)
}
}
}
struct EditorView: View {
#ObservedObject var item: Item
var body: some View {
TextEditor(text: $item.text)
}
}
This is in a macOS SwiftUI app, not an iOS one, in case there is any platform differences.
Edit: As pointed out in the comments I tried this approach Dynamic row hight containing TextEditor inside a List in SwiftUI but it didn't seem to work correctly - the rows still didn't expand properly.
To solve this problem, I ended up using the .fixedSize(horizontal: false, vertical: true) modifier to fix the TextEditor to its full size, which then allowed the solution in Dynamic row hight containing TextEditor inside a List in SwiftUI to work as expected within the list.
iOS 15
can use in init UITextView.appearance().isScrollEnabled = false
init(para: EditorParagraph) {
self._para = StateObject(wrappedValue: para)
UITextView.appearance().backgroundColor = .clear
UITextView.appearance().textDragInteraction?.isEnabled = false
UITextView.appearance().isScrollEnabled = false // here
}
var body: some View{
ZStack {
Text(para.content.isEmpty ? para.placeholder : para.content)
.font(.system(size: para.fontSize, weight: para.content.isEmpty ? .thin : .regular))
.lineSpacing(6)
.opacity(para.content.isEmpty ? 1 : 0)
.padding(.all, 8)
.frame(maxWidth: .infinity, alignment: .leading)
TextEditor(text: $para.content)
.font(.system(size: para.fontSize, weight: .regular))
.foregroundColor(.black)
.lineSpacing(6)
}
}
Will be fixed in macOS 13.0 with a scrollDisabled(_disabled: Bool) function. Seems to be a major hole in TextEditor
https://developer.apple.com/documentation/swiftui/list/scrolldisabled(_:)

Is there a way to change the background of List rows in SwiftUI? [duplicate]

This question already has answers here:
How do I modify the background color of a List in SwiftUI?
(28 answers)
Closed 3 years ago.
I would like to change the background color of List rows in SwiftUI but can't quite figure it out. Here's a simpler version of the code I've written so far.
struct ContentView: View {
init() {
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black]
UINavigationBar.appearance().barTintColor = .blue
}
var body: some View {
NavigationView {
ZStack {
HStack {
List(0...10) { test in
Spacer()
self.background(Color.purple)
Text("This is a test")
Spacer()
self.background(Color.pink)
}
.background(Color.blue)
List(0...10) { test2 in
Spacer()
Text("Also a test")
.background(Color.green)
Spacer()
}
.background(Color.red)
}
}
}
.navigationBarTitle(
Text("Test"),
displayMode: .inline
)
}
}
I'd like only the cell/row backgrounds to change color, but they stay white in light mode and black in dark mode.
You can use .colorMultiply() property for that,
Code is updated for XCode 11.
Check Below Code :
init() {
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.black]
UINavigationBar.appearance().barTintColor = .blue
}
var body: some View {
NavigationView {
VStack {
HStack {
List(0..<10) { test1 in
Spacer()
Text("This is a test1")
Spacer()
}.colorMultiply(Color.pink)
List(0..<10) { test2 in
Spacer()
Text("This is a test2")
.background(Color.green)
Spacer()
}.colorMultiply(Color.green)
}.padding(.top)
}.navigationBarTitle(Text("Test"), displayMode: .inline)
}
}
Output :
One of the things that I discovered playing with lists and sections is that they are tupleview types i.e. they aren't a single view and things like border() and background() apply to the elements inside the list, not the list itself. To create a single view to modify you need to embed them in a stack.

How to remove the left and right Padding of a List in SwiftUI?

How to remove the left and right Padding of a List in SwiftUI?
Every List i create has borders to the leading and trailing of a cell.
What modifier should I add to remove this?
It looks like .listRowInsets doesn't work for rows in a List that is initialised with content.
So this doesn't work:
List(items) { item in
ItemRow(item: item)
.listRowInsets(EdgeInsets())
}
But this does:
List {
ForEach(items) { item in
ItemRow(item: item)
.listRowInsets(EdgeInsets())
}
}
Seem we can use PlainListStyle for List for iOS 14
List {
Text("Row")
}
.listStyle(PlainListStyle())
Use this modifier:
.listRowInsets(EdgeInsets(....))
However, as stated in the documentation, the insets will be applied to the view when inside a list.
Sets the inset to be applied to the view when placed in a list. (emphasis mine)
Using this modifier on the List itself will have no effect on the views inside it. You must use the modifier on the view inside the List for the modifier to have an effect.
Example usage:
List {
Text("test")
.listRowInsets(EdgeInsets(top: -20, leading: -20, bottom: -20, trailing: -20))
}
The modifier was
.listRowInsets(EdgeInsets(......))
Remove paddings
iOS 15, Xcode 13
ScrollView {
LazyVStack {
ForEach(viewModel.portfolios) { portfolio in
PortfolioRow(item: portfolio)
}
}
}
This gives you complete control over the list (you can also remove separator using this code). Current implementation of List doesn't provide full control and contains some issues.
Note that this is a completely different API. Both List and LazyVStack are lazy containers, but in contrast to List, LazyVStack doesn't reuse cells, which will SIGNIFICANTLY change the performance when rows are more complex views.
.listStyle(GroupedListStyle())
You should call
.listRowInsets()
for row, like this:
List(items) {
YoursRowView()
.listRowInsets(EdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10))
}
The above solutions did not solve for me because my List had sections.
This solved for me:
List {
ForEach(years, id:\.self) { year in
Section(header: SectionHeader(year)) {
VStack(alignment:.leading) {
ForEach(cars[year]!, id:\.self) { car in
ListItem(car)
.frame(width: UIScreen.main.bounds.size.width,
alignment: .center)
.listRowInsets(.init())
}
}
}
.frame(width: UIScreen.main.bounds.size.width,
alignment: .center)
.listRowInsets(.init())
.listStyle(GroupedListStyle())
}
}
In resume, you have to repeat the command for the section.
2022, MacOS
in case of other solutions will not work (and they at the moment does not [for macOS]), you can use the following hack for macOS:
List(selection: $selectedItems) {
// some content
}
// solution
.padding(EdgeInsets(top: -10, leading: -20, bottom: -10, trailing: -20))
.clipShape(Rectangle())
import SwiftUI
struct ContentView: View {
enum Flavor: String, CaseIterable, Identifiable {
var id: String { self.rawValue }
case sample1, sample2, sample3
}
var body: some View {
VStack {
GeometryReader { geometry in
List {
ForEach(Flavor.allCases, id: \.self) { flavour in
Text(flavour.rawValue)
.frame(width: geometry.size.width,
alignment: .leading)
.listRowInsets(.init())
.border(Color.red, width: 1)
}
}
}
}
}
}
It definitely works I tested
Please like this :-)