SwiftUI - Accessing iOS 13's semantic colors - swiftui

iOS 13 introduces semantic colors: a way of specifying what a color's purpose is rather than its actual value. This allows the color to automatically adapt when dark mode is enabled.
In UIKit, these colors can be easily accessed via static members on UIColor (e.g. UIColor.label(), UIColor.secondaryLabel(), etc.). An extensive list of all the available semantic colors can be found on this documentation page.
However, SwiftUI's Color type does not have the equivalent static members. Therefore, this would be invalid:
// Error: Type 'Color?' has no member 'secondaryLabel'
var body: some View {
Text("Hello World!")
.color(.secondaryLabel)
}
How would I go about accessing these semantic colors in SwiftUI?

The Color struct in SwiftUI has a UIColor initialiser. So for example you could change the Text foregroundColor using the label UIColor.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Text("Hello World")
.foregroundColor(Color(.label))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
ContentView()
.environment(\.colorScheme, .dark)
}
}
}

While Color does not have static members for these semantic colors, according to the documentation, Color does have an initializer which allows you to specify an AppearanceColorName.
However, I say "according to the documentation" because, as of now, this initializer appears to be unavailable in Xcode. In a subsequent beta release, when this initializer becomes available, it can be used like this:
var body: some View {
Text("Hello World!")
.color(Color(apperanceName: .secondaryLabel))
}

Related

How to use Binding<Color> in SwiftUI?

How to change a color dynamically in SwiftUI, like in this example
#Binding var randomColor: Color
public var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20)
.opacity(0.5)
.foregroundColor($randomColor)
}
}
This does'nt work because .foregroundColor only takes Color. Is there any way of using #Binding with colors or is this just not the way of doing it in SwiftUI?
You only need to use the $variableName form of a binding variable in subviews that need to change, as well as read, the variable’s value.
If all your subview is doing is reading the currently set value, you would access variableName directly. So your body in the example you provided would be
public var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20)
.opacity(0.5)
.foregroundColor(randomColor)
}
}
Furthermore, using #Binding says that your view is going to need to edit the value of a state variable that is owned by the parent’s view. If you’re not making any changes to it, there is no point declaring it as a binding at all.
On the other hand, if you are going to be changing the value of the variable, but this view “owns" the variable, you’d use #State instead.
For example:
struct ContentView: View {
// owned by this view (and with a starting value defined)
#State var myColor: Color = .accessColor
var body: some View {
VStack {
// This subview is allowed to change the value, so it takes a binding
ColorPicker("Color", selection: $myColor)
// This subview is only going to display the color, so it takes a read-only property
ExampleColorView(color: myColor)
}
}
}
struct ExampleColorView: View {
var color: Color
var body: some View {
ZStack {
Circle()
.stroke(lineWidth: 20)
.opacity(0.5)
.foregroundColor(color)
}
}
}
Whenever the parent view’s myColor variable changes, the SwiftUI system creates a new ExampleColorView struct with the new color defined – and it spots the difference between the old view and the new one, and redraws the screen.

What's the default color for the background of a SwiftUI List?

I know how to change the background color of a SwiftUI Views list, but I cannot find the default color. I've tried using the MacOS 'Digital Color Meter', but it just doesn't pick it up right.
As you can see in this image, I've tried to set the background color of a list row (using .listRowBackground to the exact same of the surrounding list based off of the values from Digital Color Meter.
Does anyone actually know what the default background color actually is?
Short answer: it looks like it is UIColor.secondarySystemBackground
Long answer:
I have tried Digital Color Meter app the sam as you did. It shows this RGB values: 242, 242, 247.
I have created such color:
Here is my code:
import SwiftUI
extension Color {
public static let ListBGColor = Color("ListBGColor")
}
struct ContentView: View {
var body: some View {
List {
ForEach(0..<5) {_ in
Text("Hello, world!")
.padding()
}
.listRowBackground(Color.ListBGColor)
}
.listStyle(InsetGroupedListStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Result: the same color as the background (testes on Simulator and iPhone):
The default color is different for each ListStyle and platform and I don't think SwiftUI exposes these colors. You could technically Introspect the list view and get it's background color through UIKit.
If you're talking about List{ }, it is .systemGroupedBackground. Check in dark mode: .systemSecondaryBackground does something different.

SwiftUI List Background color [duplicate]

This question already has answers here:
SwiftUI List color background
(9 answers)
Closed 3 years ago.
I am trying on setting a view background color to black with the following code
struct RuleList: View {[![enter image description here][1]][1]
private var presenter: ConfigurationPresenter?
#ObservedObject
private var viewModel: RowListViewModel
init(presenter: ConfigurationPresenter?, viewModel: RowListViewModel) {
self.presenter = presenter
self.viewModel = viewModel
}
var body: some View {
List(viewModel.configurations) { configuration in
RuleListRow(website: configuration.website).background(Color.black)
}.background(Color.black)
}
}
struct RuleListRow: View {
var website: Website
#State private var websiteState = 0
var body: some View {
VStack {
Text(website.id).foregroundColor(.white)
Picker(website.id, selection: $websiteState) {
Text("Permis").tag(0)
Text("Ascuns").tag(1)
Text("Blocat").tag(2)
}.pickerStyle(SegmentedPickerStyle()).background(Color.crimson)
}.listRowBackground(Color.green)
}
}
The view is hosted in a mixed UIKit - SwiftUI storyboard, so this specific view is embed in a Hosting controller
class ConfigurationHostingController: UIHostingController<RuleList> {
private var presenter: ConfigurationPresenter = ConfigurationPresenter()
required init?(coder: NSCoder) {
super.init(rootView: RuleList(presenter: presenter, viewModel: presenter.rowListViewModel))
}
}
I've tried any combination of .background, .listRowBackground(Color.black) and .colorMultiply(.black) I could think of, and the best I got is this
iOS 16
Since Xcode 14 beta 3, You can change the background of all lists and scrollable contents using this modifier:
.scrollContentBackground(.hidden)
You can pass in .hidden to make it transparent. So you can see the color or image underneath.
iOS 14
In iOS 14, you may consider using LazyVStack instead of list for this:
ScrollView {
LazyVStack {
ForEach((1...100), id: \.self) {
Text("Placeholder \($0)")
}
}
.background(Color.yellow)
}
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
All SwiftUI's Lists are backed by a UITableViewin iOS. so you need to change the background color of the tableView. But since Color and UIColor values are slightly different, you can get rid of the UIColor.
struct ContentView: View {
init() {
/// These could be anywhere before the list has loaded.
UITableView.appearance().backgroundColor = .clear // tableview background
UITableViewCell.appearance().backgroundColor = .clear // cell background
}
var body: some View {
List {
,,,
}
.background(Color.yellow)
}
}
Now you can use Any background (including all Colors) you want
Note that those top and bottom white areas are safe are and you can use .edgesIgnoringSafeArea() modifier to get rid of them.
⚠️ Important note!
Apple is on its way to deprecate all UIKit tricks that we are using in the SwiftUI (like tweaking the UIAppearance). So you may want to consider adapting your code to the latest iOS always

SwiftUI - Picker not working when inside a Form (iPad split view)

I'm using Xcode 11 GM
The hierarchy I have is:
List > Form (Picker)
This is the code for List:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView{
List {
NavigationLink(destination: FormView())
{
Text("Item 1")
}
}
.navigationBarTitle("List")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
And this is the code for the Form:
import SwiftUI
struct FormView: View {
var body: some View {
Form {
Picker(selection: .constant(1), label: Text("Picker")) {
Text("1").tag(1)
Text("2").tag(2)
}
}
.navigationBarTitle("Form")
}
}
struct FormView_Previews: PreviewProvider {
static var previews: some View {
FormView()
}
}
The problem is:
When I build on iPad split view, tap to select works as expected:
But, when inside tags I cannot select them, nor it will go back to form view:
On iPhone, it works fine...
Is this a known bug?
Cheers to all
In my experience, it seems that the picker cells expect the user to tap on the Text elements themselves.
I don't know why. I think it's weird, too, and probably a bug. Not sure Apple knows about it yet. I sure haven't filed a radar.
To confirm this, try testing with longer text in the picker items.
What I normally do is implement a simple picker myself (not that hard, just a List and some items) and implement the items as Buttons. (If it changes the color on me, I change it back using .foregroundColor(.primary)). What's nice about this is that a Button inside a Form or a List renders as the table view cell we're used to, with the same highlighting interaction we all know and love!

How to set SwiftUI preview canvas background?

I am working on a UI component which has a whitish back and is hard to see from the SwiftUI preview canvas.
Is there a way I can set the background of the preview canvas so I can see the render UI component with a light color background?
You actually don't need the rectangle with a ZStack:
static var previews: some View {
ZStack {
Color(.black)
SampleUI()
}
}
If you're previewing on an iPhone X or above, here's how you make the color display over the safe area:
static var previews: some View {
ZStack {
Color(.black).edgesIgnoringSafeArea(.all)
SampleUI()
}
}
I managed to achieve it with a workaround by using a ZStack with a Rectangle view.
static var previews: some View {
ZStack {
Rectangle()
.foregroundColor(.black)
SampleUI()
}
}