How to make a List background black in SwiftUI? - swift

What I did to make the entire screen black is to use this construction in my view:
ZStack {
Color.black.ignoresSafeArea() }
And for the List modifiers I used the following two:
.listRowBackground(Color.black)
.listStyle(.plain)
This works great.
But then I want to add a view on top of my List (like a header) and some white space appears around it ? Where does that come from ? And how can I change it to black ?
Here is my complete code:
struct WorkoutView: View {
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
List {
TempView()
ForEach(1..<30) { index in
VStack(alignment: .leading, spacing: 4) {
Text("one line")
.foregroundColor(.white)
.font(.title)
.fontWeight(.bold)
}
.padding(.vertical)
}
.listRowBackground(Color.black)
}
.listStyle(.plain)
}
}
}
struct TempView: View {
var body: some View {
ZStack(alignment: .leading) {
Color.black.ignoresSafeArea()
Text("Hello World")
.foregroundColor(.red)
.font(.largeTitle)
.bold()
}
}
}

Just do the same thing you did for the ForEach: add a .listBackground() modifier. Like this:
TempView()
.listRowBackground(Color.black)

Related

SwiftUI View scrolls horizontal and vertical as well, instead of scrolling only vertically

I am building an app as a learning project. I ran into a problem, where one of my tabs scrolls vertically and horizontally for no reason.
There is no content outside the safe-area, and i can´t think of a reason for it to be scrolling sideways.
I already tried different things, replacing the GeometryReader with a VStack, specifying that i want it to be a vertical ScrollView. Adding .clipped to the end of the View as well as removing or replacing several other views
But nothing worked.
About the project:
The View is wrapped in a TabView and a NavigationView. All of the other tabs were wrapped the same way, and one also uses a ScrollView, yet does not have this problem.
The main Code:
struct InformationView: View {
#EnvironmentObject var model: ViewModel
var radialColor1 = Color.init(red: 225/255, green: 210/255, blue: 164/255)
var radialColor2 = Color.init(red: 233/255, green: 222/255, blue: 188/255)
var isShowingWarning = true
var body: some View {
GeometryReader { geo in
ScrollView(.vertical) {
VStack(alignment: .leading) {
Text("Here is a Text")
.padding(.vertical)
// W-fragen Stack
W_FragenCardView()
Text("Here is another Text")
.padding(.vertical)
Text("Kontakt:")
.font(.title3)
.bold()
ContactView()
}
.padding(.horizontal)
if isShowingWarning {
ZStack {
Rectangle()
.foregroundColor(Color.customRed())
Text("Here is a third Text")
.minimumScaleFactor(0.1)
.lineLimit(10)
.padding()
}
.frame(height: geo.size.height/5)
// .frame(height: 100)
}
Text("Current Theme: \(model.themes[1]!)")
}
}
.navigationBarTitle("Informationen", displayMode: .automatic)
}
}
This is were its wrapped:
struct ContentView: View {
#EnvironmentObject var model: ViewModel
var body: some View {
TabView {
NavigationView {
AnmeldungsView()
}
.tabItem {
Image(systemName: "square.and.pencil")
Text("Anmelden")
}
NavigationView {
FotosView()
}
.tabItem {
Image(systemName: "photo.fill.on.rectangle.fill")
Text("Fotos")
}
NavigationView {
InformationView()
}
.tabItem {
Image(systemName: "info.circle")
Text("Informationen")
}
}
}
}
And here an Image of how it looks when scrolling.
I hope this is sufficent to understand my problem. Any help or ideas are greatly appreciated.
P.S.: I´m new to stack overflow, so if a violated any rules please tell me :)
You should write an array([.horizontal, .vertical]) in ScrollView instead of .vertical.
change your code to this:
GeometryReader { geo in
ScrollView([.horizontal, .vertical]) {
VStack(alignment: .leading) {
Text("Here is a Text")
.padding(.vertical)
W_FragenCardView()
Text("Here is another Text")
.padding(.vertical)
Text("Kontakt:")
.font(.title3)
.bold()
ContactView()
}
.padding(.horizontal)
if isShowingWarning {
ZStack {
Rectangle()
.foregroundColor(Color.red)
Text("Here is a third Text")
.minimumScaleFactor(0.1)
.lineLimit(10)
.padding()
}
.frame(height: geo.size.height/5)
// .frame(height: 100)
}
}
And I hope it will works :)
It was a problem between geo.size.height/5 and the navigationBarTitle being on displayMode: .automatic.
This resulted in the available height changing and somehow bugged out. Either changing displayMode: to .inline or .large, or you replace the geo.size.height/5 w/ a hardcoded Int, i used 150.

Why is my SwiftUI VStack not aligning properly?

I'm trying to make my VStack padding consistent regardless of the content, and it doesn't seem to be working in certain instances (as if the Scrollview aligned with the center instead).
Ideally, I would like the padding to be consistent regardless of the content on the screen.
Such as this (when padding isn't consistent):
Here is the code used:
var body: some View {
VStack {
HStack{
Button(action: {}) {
Image(systemName: "arrow.backward")
}
Spacer(minLength: 0)
Button(action: {}) {
Image(systemName: "bookmark")
}
}
.padding(.vertical, 10)
ScrollView {
VStack{
HStack{
VStack (alignment: .leading, spacing: 5) {
Text("Today's Poem, \(currentDate)")
if let poem = fetch.poems.first {
Text("\(poem.title)")
Text("BY "+poem.author.uppercased())
VStack (alignment: .leading) {
ForEach(poem.lines, id: \.self) {
Text($0)
.multilineTextAlignment(.leading)
}
}
} else {
Spacer()
}
}
}
}
}
Button("Get Next Poem") { fetch.getPoem() }
}
.background(Color.white.ignoresSafeArea())
.padding(.horizontal)
}
}
Any ideas? Thanks in advance.
Change the innermost
VStack {
to
VStack(alignment: .leading) {

SwiftUI macOS List Row removing padding

I am trying to display a row inside my ListView in SwiftUI for MacOS.
However, my issue is that the row contains padding around it. I would like to stretch it to the boundaries of my ListView.
Apple uses .listRowInsets(EdgeInsets()) in their example. However, that is only shown on iOS. For me, it is not working on macOS.
My row got a red border to visualize the issue. I want it to be stretched all the way to the boundaries of the List Row, so fill the whole blue row.
Is that possible in macOS?
Thanks in advance.
For now I've found only workaround (.listRowInsets should really do this work, so worth submitting feedback to Apple):
struct TestListRow: View {
var body: some View {
List {
ForEach (0..<3) { i in
HStack {
Text("Test row \(i)").font(.largeTitle)
Spacer()
}
}
.listRowBackground(Color.green)
.border(Color.red)
.padding(.horizontal, -8) // << workaround !!
}.environment(\.defaultMinListRowHeight, 40)
.border(Color.yellow)
}
}
I added this code at end of the row and the padding was shrunk!!!
Spacer()
Text(" ")
.padding()
Sample Code
import SwiftUI
struct testViews1: View {
var body: some View {
List {
ForEach (0..<3) { i in
HStack {
Text("row \(i)")
Spacer()
}
.background(Color.red)
.frame(height: 30)
}
}
}
}
struct testViews2: View {
var body: some View {
List {
ForEach (0..<3) { i in
HStack {
Text("row \(i)")
Spacer()
Text(" ")
.padding()
}
.background(Color.red)
.frame(height: 30)
}
}
}
}
struct testViews_Previews: PreviewProvider {
static var previews: some View {
testViews1()
.frame(height: 150)
testViews2()
.frame(height: 150)
}
}
output
It works with SwiftUI 1.

SwiftUI content is not displayed on top of the screen

I have a NavigationView and then a VStack. The issue is that everything inside VStack is being displayed in the middle of the screen and not on top. Why is that?
var body: some View {
VStack {
VStack(alignment: .leading) {
if (request?.receiverID ?? "") == userDataViewModel.userID {
Text("\(sendertFirstName) wants to ship your package").font(.title)
} else {
Text("You want to ship \(recieverFirstName)'s package").font(.title)
}
HStack{
Image(systemName: "clock.fill").font(.subheadline)
Text("\(createdAt, formatter: DateService().shortTime)").font(.subheadline).foregroundColor(.secondary)
}
HStack {
VStack(alignment: .leading) {
HStack {
Image(systemName: "person.fill").font(.subheadline)
Text(sendertFirstName).font(.subheadline).foregroundColor(.secondary)
}
}
Spacer()
VStack(alignment: .leading) {
HStack {
Image(systemName: "star.fill")
Text(rating).font(.subheadline).foregroundColor(.secondary)
}
}
}
}
VStack(alignment: .center) {
HStack {
Button("Accept") {
//print(self.request.createdAt)
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "accepted")
}
.padding()
Button("Decline") {
//FirestoreService().respondToRequest(request: self.request.documentReference, status: "declined")
}
.foregroundColor(.red)
.padding()
}
}
ScrollView {
ForEach(messages) { (message: Message) in
if message.senderId == self.userDataViewModel.userID {
HStack {
Spacer()
Text(message.text).font(.subheadline).padding(7.5).background(self.blue).cornerRadius(30).foregroundColor(.white)
}.padding(.bottom, 2)
} else {
HStack {
Text(message.text).font(.subheadline).padding(7.5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Spacer()
}.padding(.bottom, 2)
}
}
}
HStack {
TextField("Message", text: $inputMessage).padding(5).background(self.gray).cornerRadius(30).foregroundColor(.white)
Button(action: {
let msg: [String: Any] = [
"text": self.inputMessage,
"createdAt": Timestamp(),
"senderId": self.userDataViewModel.userID
]
self.reference.updateData([
"messages": FieldValue.arrayUnion([msg])
])
self.messages.append(Message(dictionary: msg))
self.inputMessage = ""
}) {
Image(systemName: "arrow.up.circle.fill").foregroundColor(self.blue).font(.title)
}
}
Spacer()
}
.padding()
.border(Color.red)
}
I want the content to start on top not on the middle. What am I doing wrong here?
You don't need the NavigationView - the way you have everything set up now you are wrapping the VStack in a NavigationView twice and the massive white space is the second empty navigation bar.
By default most Views take only the amount of space they need and they are placed in the center of their parent view. So if you want your content to be placed at the top, you need to add Spacer() as the last element in your VStack:
var body: some View {
VStack {
/* ... */
Spacer()
}
}
Spacer is one of the rare Views that takes all space offered to it by the parent view and will push all of your content upwards.
Maybe you can try this way:
NavigationView {
VStack {
...
}.frame(maxHeight:.infinity, alignment: .top)
.padding()
.border(Color.red).navigationBarTitle("", displayMode: .inline)
}
Add a .navigationBarHidden(true) to your VStack

Views compressed by other views in SwiftUI VStack and List

In my SwiftUI application, I'm trying to implement a UI similar to this:
I've added the two rows for category 1 and category 2. The result looks like this:
NavigationView {
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
Spacer()
}
.navigationBarTitle(Text("Featured"))
}
Now, when added the view for the third category – an VStack with images – the following happens:
This happened, after I replaced Spacer(), with said VStack:
VStack(alignment: .leading) {
Text("Rivers")
.font(.headline)
ForEach(self.categories["Rivers"]!.identified(by: \.self)) { landmark in
landmark.image(forSize: 200)
}
}
My CategoryRow is implemented as follows:
VStack(alignment: .leading) {
Text(title)
.font(.headline)
ScrollView {
HStack {
ForEach(landmarks) { landmark in
CategoryItem(landmark: landmark, isRounded: self.isRounded)
}
}
}
}
Question
It seems that the views are compressed. I was not able to find any compression resistance or content hugging priority modifiers to fix this.
I also tried to use .fixedSize() and .frame(width:height:) on CategoryRow.
How can I prevent the compression of these views?
Update
I've tried embedding the whole outer stack view in a scroll view:
NavigationView {
ScrollView { // also tried List
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
ForEach(...) { landmark in
landmark.image(forSize: 200)
}
}
.navigationBarTitle(Text("Featured"))
}
}
...and the result is worse:
You might prevent the views in VStack from being compressed by using
.fixedSize(horizontal: false, vertical: true)
For example:
I have the following VStack:
VStack(alignment: .leading){
ForEach(group.items) {
FeedCell(item: $0)
}
}
Which render compressed Text()
When I add .fixedSize(horizontal: false, vertical: true)
it doesn't compress anymore
VStack(alignment: .leading){
ForEach(group.items) {
FeedCell(item: $0)
.fixedSize(horizontal: false, vertical: true)
}
}
You could try to add a layoutPriority()operator to your first VStack. This is what the documentation says about the method:
In a group of sibling views, raising a view’s layout priority encourages that view to shrink later when the group is shrunk and stretch sooner when the group is stretched.
So it's a bit like the content compression resistance priority in Autolayout. But the default value here is 0, so you just have to set it to 1 to get the desired effect, like this:
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
Spacer()
}.layoutPriority(1)
VStack(alignment: .leading) {
...
}
Hope it works!
It looks like is not enough space for all your views in VStack, and it compresses some of them. You can embed it into the ScrollView
NavigationView {
ScrollView {
VStack(alignment: .leading) {
CategoryRow(...)
CategoryRow(...)
/// you images and so on
}
}
}
struct ContentView1: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
CategoryListView {
CategoryView()
}
CategoryListView {
SquareCategoryView()
}
CategoryListView {
RectangleCategoryView()
}
}
.padding()
}
.navigationTitle("Featured")
}
}
}
struct CategoryListView<Content>: View where Content: View {
private let viewSize: CGFloat = 150
var content: () -> Content
init(#ViewBuilder content: #escaping () -> Content) {
self.content = content
}
var body: some View {
VStack {
HStack {
Text("Category name")
Spacer()
}
ScrollView(.horizontal, showsIndicators: false){
HStack {
ForEach(0..<10) { _ in
content()
}
}
}
}
}
}
struct ContentView1_Previews: PreviewProvider {
static var previews: some View {
ContentView1()
}
}
struct CategoryView: View {
private let viewSize: CGFloat = 150
var body: some View {
Circle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize)
}
}
struct RectangleCategoryView: View {
private let viewSize: CGFloat = 350
var body: some View {
Rectangle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize * 9 / 16)
}
}
struct SquareCategoryView: View {
private let viewSize: CGFloat = 150
var body: some View {
Rectangle()
.fill()
.foregroundColor(.blue)
.frame(width: viewSize, height: viewSize)
}
}
I think your topmost view (in the NavigationView) needs to be a List, so that it is scrollable:
NavigationView {
List {
...
Or use a ScrollView.
A stack automatically fits within a screen. If you want your content to exceed this, you would have used a ScrollView or a TableView etc i UIKit
EDIT:
Actually, a little Googling brought this result, which seems to be exactly what you are making:
https://developer.apple.com/tutorials/swiftui/composing-complex-interfaces