SwiftUI large Image in ZStack breaks alignment of other layers - image

I have a simple ContentView consisting of a VStack with some text and a spacer.
VStack {
Text("Hello")
Spacer()
Text("World")
}
I want to put a background image behind it. I tried using ZStack.
ZStack {
Image("background")
VStack {
Text("Hello")
Spacer()
Text("World")
}
But now the VStack doesn't respect the safe area anymore.
Also in landscape mode the VStack doesn't rescale at all, effectively moving its contents out of view.
Why does the Image affect the VStack? I thought the ZStack keeps layers "independent"?

What's happening here is correct, if the size of the "background" image is large and roughly the size of the screen.
The size of the ZStack view is determined by the size of its layers. It's made big enough to just encapsulate them. Here it looks like the overall size is determined by the "background" image size. That in turn effects the size of the VStack layer because its Spacer will expand to fill to that size.
Resize your Image to what you want. Also look at using the background() modifier on the VStack instead of a ZStack.
How you want the Image to look in landscape mode is up to you, but it won't resize or flip by itself.

Related

Simple (but complex) layout design with SwiftUI

I'm trying to code a simple layout design with SwiftUI without success!
Here's what I'd like to do:
ScrollView {
VStrack {
// Header with an orange background.
// This orange color should also apply to the status bar.
}
VStrack {
// Content with a white background.
// This white color should always go to the bottom.
}
}
I first tried to apply an orange background to the first VStack and a white background to the second VStack but I couldn't color the status bar in orange even with .ignoresSafeArea().
Then I tried to apply an orange background to the ScrollView and a white background again to the second VStack but I couldn't color the bottom of the screen in white even with .infinity.
I also tried to use LazyVStack but nothing happened.
Do you guys have any idea to make this works? :-)
You can try setting the height of the content to be at least the screen height:
.frame(minHeight: UIScreen.screenHeight)
Here's the sample code of the full view's body (you can replace the HStacks with VStacks, as long as you fill the width):
ScrollView {
HStack {
Spacer()
Text("Header")
Spacer()
}
.padding()
.background(.orange)
HStack {
Spacer()
Text("Content")
Spacer()
}
.padding()
.frame(minHeight: UIScreen.screenHeight, alignment: .top)
.background(.white)
}
.background(Color.orange.ignoresSafeArea(.all, edges: .top))

SwiftUI - showing an image in a List/Form without a border around the cell

I'm trying to create a List or Form in SwiftUI to display a few fields. One of them is an image. I'd like it to show without any borders in the cell but the content of the row seems to contain some sort of inherent margin inside the cell that I can't get rid of.
This is my code at the moment...
List {
Section("Photo") {
Button {
// do a thing
} label: {
Image(uiImage: image)
.resizable()
.aspectRatio(1, contentMode: .fit)
}
}
.listRowBackground(Color.gray)
TextField("Name", text: viewStore.binding(\.$name))
DatePicker("DOB", selection: viewStore.binding(\.$dob), displayedComponents: [.date])
}
And this is what it looks like...
Really there are two problems here.
How do I get rid of the grey border so that the image fills to the extent of the cell?
How do I resize the image with what in UIKit would be scaleAspectFill? It is currently squeezing the image to fit.
OK... I had some ideas while typing out the question.
I've solved this (at least partly) by providing the listRowInsets on the Section.
Section {
// the content
}
.listRowInsets(.init(top: 0, leading: 0, bottom: 0, trailing: 0))
This then displays like...
Now to work on fixing the aspect ratio. 🤣
I fixed the aspect ratio using this answer...
How to center crop an image in SwiftUI

How do I indicate to swiftUI ScrollView that content size has changed?

I am writing a swiftUI app for macOS.
Consider this situation: I have a scroll view containing an image that is resizable. If I resize the window to smaller than the image, the scroll views bars appear as expected. If I use .scaleEffect(someStateVar) to resize the image (by changing someStateVar), the scroll view content does not update to the new size of the image.
Other than wrapping NSScrollView into a swiftUI view, how do I indicate to the swiftUI scrollview that the content size has changed?
Example that shows scaling the image does not set the "content size" of the scrollview. When you size the clock to large than the scroll view, the scroll bars do not appear and you cannot scroll. When you try to scroll, you briefly see the hidden areas but the view jumps back to the original location.
import SwiftUI
struct MyImageView: View {
#State var scale: CGFloat = 1
var theImage: some View{
ScrollView([.horizontal, .vertical]){
VStack{
HStack{
Image(systemName: "clock.fill")
.scaleEffect(scale)
}
}
}
}
var body: some View {
theImage
Slider(value: $scale, in: 1...1000)
}
}
struct ContentView: View {
var body: some View {
MyImageView()
}
}
You can use the .frame modifier to explicitly set the size of the VStack. The downside is that you have to know the full dimensions.
var theImage: some View{
ScrollView([.horizontal, .vertical]){
VStack{
HStack{
Image(systemName: "clock.fill")
.scaleEffect(scale)
}
}
.frame(width: 50 * scale, height: 50 * scale, alignment: .center)
}
}

SwiftUI endless gradient background

I want to make a scrolling app with a gradient background. As user scrolls - background color changes.
For example, the bottom is black and the top is white I would specify the height of a VStack for 8000, and for this height, as the user scrolls the screen he will see the color change.
I didn't find any solution. Tried making LinearGradient for VStack and Rectangle figures for its full height, but it only covers the phone screen size (can't fill all 8000 height points). So if I scroll upwards, the gradient is lost and it only displays the black screen.
Thank you]1
You can achieve this using a rectangle within a ZStack:
struct ContentView: View {
var body: some View {
ScrollView {
ZStack(alignment: .top) {
// Example width and height. Width should be the width of the device
Rectangle().frame(width: 10000, height: 2000).foregroundColor(.clear).background(LinearGradient(gradient: Gradient(colors: [.white, .black]), startPoint: .top, endPoint: .bottom))
// Add your actual content here:
Text("Yeet")
}
}
}
}

SwiftUI: How to clip just the bottom of an image

I have an image which I wish to pin to the top of the view with a height of 200. I started with the following:
struct ContentView: View {
var body: some View {
VStack {
Image("frog")
.resizable()
.scaledToFill()
.frame(height:200)
Spacer()
}
}
}
which gives me:
You can see the frame (with height 200) outlined in blue. Now, I want the image to continue to spill out of the safe zone to fill the top of the view, as it is doing. But I want clip the image at its bottom frame, so I get something like this:
I'd also be fine with something like this, where the whole image is shifted upwards to where the natural bottom of the image is at the bottom of the frame:
I've tried a wide array of modifiers, as well as using GeometryReader but have not been able to achieve either result. I need this to work for images of arbitrary dimensions.
To shift the image upwards you can do the following:
.frame(height:200, alignment: .bottom)
Here is alternate that gives you effect of your 2nd screenshot:
Image("frog")
.resizable()
.scaledToFill()
.frame(height:200)
.mask(Rectangle().edgesIgnoringSafeArea(.top)) // << here !!

Resources