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
Related
As a hobby project, I'm developing a SwiftUI app targeted for macOS.
I have a CoreData entity (let's call it Sample) with a String property called title.
In my main view (SamplesView) I'm displaying a List of Samples, and I want titles be editable directly from the list. For that, I've made a sub-view (SampleRowView) with a TextField, and I'm displaying this sub-view in the List using ForEach.
It works and looks okayish. Though, I can edit the title only if I click directly on the TextField's text (point 1 on the screenshot). If I click on the "empty" part of the TextField (f.e. point 2) it does not respond. I thought that the shape of the TextField is limited somehow by the length of its text, but as visible on the screenshot, TextField occupies the whole row.
Appreciate any help and ideas about how to make the TextField respond to click on its any point, not only on the text.
// "Sample" is a CoreData entity
public class Sample: NSManagedObject {
//...
#NSManaged public var title: String
}
// This is the main view
struct SamplesView: View {
#FetchRequest(...)
var samples: FetchedResults<Sample>
var body: some View {
VStack {
List {
ForEach(samples) { sample in
SampleRowView(sample: sample)
}
.onDelete(perform: deleteSample)
}
}
}
}
// List rows with editable Sample's title
struct SampleRowView: View {
#ObservedObject var sample: Sample
var body: some View {
TextField("", text: $sample.title)
}
}
Update:
The problem is the same even on the fresh project. Also, if I change TextField with TextEditor the behavior is kinda expected.
Digging a bit more into it:
TextField inside a List in SwiftUI on macOS: Editing not working well
Editable TextField in SwiftUI List
SwiftUI make ForEach List row properly clickable for edition in EditMode
I've found that it seems to be a bug in SwiftUI, and for now the only solution is to somehow replace the List with ScrollView with custom item moving and deletion. This is sad.
import SwiftUI
struct Sample: Identifiable {
let id: Int
var title: String
init(id: Int) {
self.id = id
self.title = "Sample \(id)"
}
}
struct TestView: View {
#State var samples = [Sample(id: 1), Sample(id: 2)]
var body: some View {
List {
ForEach($samples) { $sample in
TextField("", text: $sample.title) // .textFieldStyle(.squareBorder) -- doesn't help
// TextEditor(text: $sample.title) // This works as expected
}
}
}
}
#main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
TestView()
}
}
}
I'm using XCode Version 13.2.1, Swift 5, MacOS deployment target 11.6.
You could try to add
.contentShape(Rectangle())
to your View element.
I use it along with Text()-Instances which allows me to accept clicks not only on the written part of the View element, but everywhere within its bounds.
I want to replicate the layout of the time label in the iOS Camera app in a SwiftUI view.
My test code is this:
class TimeTestModel:ObservableObject {
#Published var seconds:Int = 0
var timer:Timer?
init() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(TimeTestModel.tick), userInfo: nil, repeats: true)
}
#objc func tick() {
seconds += 1
}
}
struct TestView: View {
#StateObject var timeModel = TimeTestModel()
var body: some View {
VStack() {
HStack {
Text("00:0\(timeModel.seconds)")
}
.border(.red)
}
}
}
It creates this:
What I want is this:
Using my code you can see how the whole text moves left to right as new text is rendered (I have only done from 0 to 9 for the sake of the example, I know I need more String work to go beyond that...).
In the iOS camera app version you can see the positioning of the whole label does not change as the time changes.
I am not sure how to achieve this in SwiftUI.
How can I change my code to allow digits to update without causing horizontal shift?
Like Yrb mentioned in his comments, this can be solved using a non proportional Font. So I achieved the wanted result by making this change:
VStack {
HStack {
Text(timeModel.timeString)
.font(Font.system(size: 12, weight: .medium).monospacedDigit()). <<-- HERE!
}
.border(.red)
}
Now the Text has zero horizontal shift.
With the new XCode 13 and it‘s iOS 15 support the presentation of Lists have apparently changed.
Now a List has an additional gray background. Before, the background was plain white, just as I would like it to be. When I add other elements like texts, the default background color is still white.
Is there any way to get rid of the gray surrounding of the List without switching to a ForEach() solution?
I tried changing the background color from gray to white on various places and adding additional stacks in hope to override the default background color.
This I want be be all white without the gray surrounding:
struct ContentView: View {
var body: some View {
VStack {
Text("Test")
List {
ForEach(1..<20) { i in
Text(String(i))
}
}.frame(maxWidth: .infinity)
}
}
}
Change the listStyle to .plain. The default for iOS 14 is .plain, and .insetGrouped for iOS 15.
Code:
struct ContentView: View {
var body: some View {
VStack {
Text("Test")
List {
ForEach(1 ..< 20) { i in
Text(String(i))
}
}
.listStyle(.plain)
}
}
}
Result:
I am trying to create a TextField that spans across the entire window. Currently it only expands to fill the view on the horizontal axis. I'm aiming to achieve something similar to that seen in TextEdit.
Here is my code so far:
struct ContentView: View {
#State var contents: String = "Hello World\nThis is a test.";
var body: some View {
VStack() {
TextField("Notes", text: $contents)
.lineLimit(nil)
}
.frame(height: 600)
}
}
Application Screenshot
Is this possible in SwiftUI or will I need to revert to using legacy AppKit components?
I've found that wrapping an NSTextView is still be the best way to achieve something similar to that seen in TextEdit. So yes, at least now you will have to use legacy AppKit components in your SwiftUI code.
struct MultilineTextView: NSViewRepresentable {
typealias NSViewType = NSTextView
#Binding var text: String
func makeNSView(context: Self.Context) -> Self.NSViewType{
let view = NSTextView()
view.isEditable = true
view.isRulerVisible = true
return view
}
func updateNSView(_ nsView: Self.NSViewType, context: Self.Context) {
nsView.string = text
}
}
struct ContentView: View {
#State var contents: String = "Hello World\nThis is a test.";
var body: some View {
VStack() {
MultilineTextView(text: $contents)
}.background(Color.white)
}
}
It seems, that there is bug with TextField line limit, according to this answer and comments to it.
I believe in this thread you can find answer, how achieve what you want.
Just replying to this as nobody's given a decent answer yet besides wrapping UIKit.
TextEditor(text: $contents)
Is the better path as it WILL behave like you wish as compared to TextField(). It automatically takes care of scrolling and expanding to the View area allowed.
TextEditor WILL require you to be on at least iOS 14. If you're not on iOS 14 then wrapping the UIKit control is your best option.
Is it possible to create a slot machine with swiftUI to show two sets of values?
In UIKit, UIPickerView provides the option to have multiple components in your picker view. SwiftUI's Picker does not. However, you can use more than one Picker in an HStack instead. The perspective may look slightly different than a UIPickerView with multiple components in some instances, but to me it looks perfectly acceptable.
Here's an example of a slot machine with 4 pickers side by side and a button that "spins" the slot-machine when tapped (note that I disabled user interaction on the pickers so they can only be spun using the button):
enum Suit: String {
case heart, club, spade, diamond
var displayImage: Image {
return Image(systemName: "suit.\(self.rawValue).fill")
}
}
struct ContentView: View {
#State private var suits: [Suit] = [.heart, .club, .spade, .diamond]
#State private var selectedSuits: [Suit] = [.heart, .heart, .heart, .heart]
var body: some View {
VStack {
HStack(spacing: 0) {
ForEach(0..<self.selectedSuits.count, id: \.self) { index in
Picker("Suits", selection: self.$selectedSuits[index]) {
ForEach(self.suits, id: \.self) { suit in
suit.displayImage
}
}
.frame(minWidth: 0, maxWidth: .infinity)
.clipped()
.disabled(true)
}
}
Button(action: self.spin) {
Text("Spin")
}
}
}
private func spin() {
self.selectedSuits = self.selectedSuits.map { _ in
self.suits.randomElement()!
}
}
}
This is just an example, and could no doubt be improved, but it's a decent starting point.
Keep in mind that this code does throw a warning in Xcode Beta 5 -
'subscript(_:)' is deprecated: See Release Notes for migration path.
I haven't had a chance to look into this, but the example still works and should help you with what you're trying to achieve.