Remove spacing between HStacks embedded in Stack - swift

I am trying to stack two HStacks filled with circles, however, no matter what I do, I cannot get rid of the spacing between the two.
VStack {
VStack(spacing: 0) {
HStack {
ForEach(viewModel.lights) { light in
Circle()
.foregroundColor(light.color)
}
}
HStack {
ForEach(viewModel.lights) { light in
Circle()
.foregroundColor(light.color)
}
}
} .padding()
Button(action: viewModel.start ) {
Text("Start")
}
}

Since the Circles have no height constraint, they are taking up all of their available vertical space, even though the visible shape doesn't take up that space. Horizontally, they're limited by the width of the device/screen.
You can add .aspectRatio(contentMode: .fit) to make them constrained vertically as well to only the space they need to take up:
Circle()
.aspectRatio(contentMode: .fit)
.foregroundColor(light.color)
Once that is done, if you also want to push them towards the top of the screen, you can add a Spacer below the HStacks.

Related

How to prevent view from being pushed by other view expanding in SwiftUI

If you have something like this. You'll see that the bottom views rectangle 1 and rectangle 2 are aligned with one another.
HStack(spacing: 0) {
Rectangle()
.fill(Color.smashFrameStartup)
.overlay(
VStack {
Text("Data").font(.system(size: 100.0))
Text("rectangle 1")
}
)
Rectangle()
.fill(Color.smashOnShield)
.overlay(
VStack {
Text("Data").font(.system(size: 100.0))
Text("rectangle 2")
}
)
}
However as soon as you make the top text longer from "Data" to "Data 2" or anything longer, you can see that the top view pushes the bottom view further down. How do I stop the bottom view from being pushed further down no matter the length of the text in the top view?
Here's the updated code and what happens.
HStack(spacing: 0) {
Rectangle()
.fill(Color.smashFrameStartup)
.overlay(
VStack {
Text("Data").font(.system(size: 100.0))
Text("rectangle 1")
}
)
Rectangle()
.fill(Color.smashOnShield)
.overlay(
VStack {
Text("Data 2").font(.system(size: 100.0))
Text("rectangle 2")
}
)
}
Is it possible to position the text view so it doesn't move? I would like to have rectangle 2 to be aligned with rectangle 1 no matter the length of the above text view on either rectangle.
I should also note that if the above text view string is long like below, it'll be truncated which I don't want.
HStack(spacing: 0) {
Rectangle()
.fill(Color.green)
.overlay(
VStack {
Text("Data").font(.system(size: 100.0))
Text("rectangle 1")
}
)
Rectangle()
.fill(Color.red)
.overlay(
VStack {
Text("Data 2 with even more information here").font(.system(size: 100.0))
Text("rectangle 2")
}
)
}
I've also tried adding minimumScaleFactor with 0.4 and it does show the whole text without it being truncated but it has the same problem where it'll push the bottom text view further down.
HStack(spacing: 0) {
Rectangle()
.fill(Color.green)
.overlay(
VStack {
Text("Data").font(.system(size: 100.0))
Text("rectangle 1")
}
)
Rectangle()
.fill(Color.red)
.overlay(
VStack {
Text("Data 2 with even more information here").font(.system(size: 100.0))
.minimumScaleFactor(0.4)
Text("rectangle 2")
}
)
}
EDIT: Criteria
Have the position of the two bottom text views in each rectangle be aligned with one another like in the first picture and at that position on the screen.
The top text view can be of variable length, whether it's 2 or up to 40 characters long, it shouldn't cause the bottom text view to be pushed further down. It should be noted that the length of the string in the top text view can be a different length compared to the top text view in the other rectangle. Despite that, the bottom views shouldn't move down and they should still be aligned with one another.
If the top view text string starts to become too long, it's ok to change the font size as long as we don't shift the bottom text view. In order to show all of the text.
The very long string in the top view shouldn't be truncated like in the third picture. I tried fixing this with minimumScaleFactor and the truncated problem is fixed like in the fourth picture but as you can see the bottom text view is still shifted.

How to remove bottom default padding from List view in SwiftUI

I'm trying to remove the bottom padding on a List view when another view is being defined after it so that the List view is sitting on top of the other one. Not overlaying. There seems to be some extra space created from the List and I'm not sure how to remove that to achieve what I want. Here's what I have.
List {
ForEach(0...2, id: \.self) { color in
Text("\(color)")
}.listRowInsets(EdgeInsets())
}
Rectangle().fill(Color.red)
Here's what that looks like. You can see that there is a gap between row 2 and the red rectangle. What I'd like is for the boundaries of those two views to be adjacent to each other.
EDIT: I've tried embedding the code in a VStack but that doesn't remove the white gap we see below.
Looking at this answer, it seems like wrapping the list in a VStack would remove the gap but it’s not from what we can see here?
VStack(spacing: 0) {
VStack(spacing: 0) {
List {
ForEach(0...0, id: \.self) { number in
Text("1")
.listRowBackground(Color.blue)
Text("2")
}.listRowBackground(Color.gray)
}
}
VStack(spacing: 0) {
HStack(spacing: 0) {
Rectangle()
.fill(Color.red)
Rectangle()
.fill(Color.red)
}
HStack(spacing: 0) {
Rectangle()
.fill(Color.red)
Rectangle()
.fill(Color.red)
}
}
.aspectRatio(1.0, contentMode: .fill)
VStack {
List {
Text("1")
.listRowBackground(Color.purple)
Text("2")
.listRowBackground(Color.pink)
Text("3")
.listRowBackground(Color.blue)
Text("4")
.listRowBackground(Color.orange)
}
}
}
I've tried multiple variations of VStacks with spacing equals 0 and haven't found how to remove that gap that's created by the List view. If there are more rows added, it begins to fill up but I'm trying to just have two rows with no white gap sit above the red rectangle.
We can see the List view highlighted below includes that gap.

SwiftUI content gets cut off on smaller iPhone screens

I'm new to SwiftUI, so I'm following a tutorial to get familiar with it. However, my app's content is getting cut off on smaller screens (both vertically and horizontally). How can I prevent this from happening?
Here's my code:
EDIT: I have added borders around my images and resized the images as suggested in your comments and answers, but as you can see, the images don't appear to be taking up any more space than they're supposed to.
struct ContentView: View {
var body: some View {
ZStack {
Image("background").ignoresSafeArea(.all)
VStack {
Spacer()
Image("logo")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
.border(Color.black)
Spacer()
HStack {
Spacer()
Image("card3").border(Color.black, width: 3)
Spacer()
Image("card4").border(Color.black, width: 3)
Spacer()
}
Spacer()
Image("dealbutton")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
.border(Color.black)
Spacer()
HStack {
Spacer()
VStack {
Text("Player").padding(.bottom, 10)
Text("0").font(.largeTitle)
}
Spacer()
VStack {
Text("CPU").padding(.bottom, 10)
Text("0").font(.largeTitle)
}
Spacer()
}
.foregroundColor(.white)
Spacer()
}
}
}
}
And here's what the preview looks like on an iPod touch:
Try making Image("background") resizable or set it as the .background(:) of your ZStack. Currently the background image isn’t resizable and is larger than the screen, so it shows at its native size and stretches its parent ZStack beyond the bounds of the screen. Since your content is in that same ZStack, it also extends beyond the bounds of the screen
Your issue is related to your images that you have present on the view structure itself. Images are rendered at 100% their size, irrespective of their constraints. This will cause other views to be pushed away. The solution for that is to set a set size on the view itself that matches within the confines of your available space. Also you're resizing your ZStack which also resizes the content inside of the ZStack. For example.
Image("logo")
.resizeable()
.aspectRatio(contentMode: .fit)
.frame(width: 100)
Handling your image like this will ensure that it is set to the appropriate size when it is rendered. Then you can have the remaining views fall into place in a way that's expected. If you need it scaled on a % for the screen size you can use a GeometryReader to scale the view for different screen sizes.
GeometryReader { reader in
Image("logo")
.resizeable()
.aspectRatio(contentMode: .fit)
.frame(width: reader.size.width * 0.2)
}
Finally, remove your ZStack and set it up like this.
VStack {
//Your Content
}.background(
//Make sure to set the edgesIgnoring.. on this NOT the VStack
Image("background").edgesIgnoringSafeArea(.all)
)
Tip When Working with Stacks
Stacks, wether VStack, HStack, or ZStack's all ALWAYS have their frame set to the content that is held inside of them. If you had an object with a width of 100 and a height of 10,000 then the Stack would also have those dimensions, unless otherwise specified with a Modifier such as .frame(width...) or even, in your case, an Image that is resized.
Suppose your same stack then has a width: 10, height: 10 view added to it, it would still retain the same size as the largest content held within. This is of course handled differently with HStack and VStacks as they actually stack things in a 2D plane, whereas the ZStack works on the 3D plane.

SwiftUI Zstack – Make element ignore safe area and another one don't

I have a Zstack like this:
ZStack {
Image("beach")
.resizable()
.edgesIgnoringSafeArea(.all)
.scaledToFill()
VStack {
// with a lot of stuff
}
}
I want the image to ignore the safe area, but the Vstack must respect the safe area.
The image is a background image that should cover all the area.
This code I have is making the VStack full screen too and I don't want that.
Why everything is not respecting the safe area is a mystery, because the respective modifier is applied to the image only.
Put your image in the .background:
struct ContentView: View {
var body: some View {
VStack {
// lots of stuff
Color.red
Color.blue
}
.background(
Image("beach")
.resizable()
.edgesIgnoringSafeArea(.all)
.scaledToFill()
)
}
}
The reason why it doesn't work with your example is that you are using scaledToFill() modifier on image, and here it matters in this particular case.
First you declared ZStack which is itself doesn't ignore safe area. After you put Image and resizable. What resizable does, is it stretches the image to fit its view, in your case it is the ZStack.
Let's see what we have until now.We have an image which stretches till the safe area.
ZStack {
Image("Breakfast").resizable()
}
So from now on you put edgesIgnoringSafeArea on Image, which lets the image cover all the area(ignore safe area).
Now you have Zstack which respects safe area, and Image which ignores safe area. This let you put VStack in ZStack and add staff inside it. VStack will fill its parent view, which is ZStack, so it too will respect safe area(See code and image below).
ZStack {
Image("Breakfast").resizable().edgesIgnoringSafeArea(.all)
VStack {
Text("hello")
Spacer()
}
}
And here at last you add .scaledToFill() modifier, which stretches the image to contain all the area, and by doing this it makes ZStack view to become the hole area, as fitting view's (ZStack, HStack, VStack) calculates its size based on its content.
Useful link:
Fitting and filling views in SwiftUI
How to resize a SwiftUI Image and keep its aspect ratio
Another way that worked for me is:
struct ContentView: View {
var body: some View {
ZStack {
Color.clear
.background(
Image("beach")
.resizable()
.ignoresSafeArea()
.scaledToFill()
)
VStack {
// lots of stuff
}
}
}
}
Note: In my case, when trying the other solution of putting .background on VStack the image did not fill the entire screen, instead it shrank to fit the size of what was in the VStack.

Swiftui how to programatically adjust spacing between images and text?

I'm trying to get an aligned text-image pairs in swiftui programmatically. Is it possible?
I want the word "Visit" to start from the same position in both rows.
This is my code:
HStack{
Image(systemName: item.leftImage) // A system image name
Text(item.text) // A text representing the item
}
I know that I can add spacing to the HStack itself, but it won't fix my issue.
Since every systemimage provided by apple has a different sizing, I guess that I should programmatically somehow determine the spacing that should be instantiated between each the image to the text.
How can I do that?
I would recommend to use explicit width for image frame, this will fit any symbol automatically and make it centred
HStack{
Image(systemName: item.leftImage)
.frame(width: 28)
Text(item.text)
}
Give the Image a constant frame, like below:
HStack {
Image(systemName: item.leftImage)
.frame(width: 32)
Text(item.text) // A text representing the item
}
HStack takes Vertical alignment as argument. Then the text and icon is vertical alignment.
HStack(alignment: .center)
{
Image(systemName: item.leftImage) // A system image name
Text(item.text) // A text representing the item
}
To align the text, give the image a frame width. You need to determine they maximum width a image can take.
VStack(alignment: .leading)
{
HStack(alignment: .center)
{
Image(systemName: "pause") // A system image name
.frame(width: 50)
Text("Test") // A text representing the item
}
HStack(alignment: .center)
{
Image(systemName: "cloud") // A system image name
.frame(width: 50)
Text("Test") // A text representing the item
}
}