How to fix padding in ZStack - swiftui

I have ZStack with two views one of which can be shown due to the condition:
ZStack{
VStack{
...
}.padding(.all,17)
if handler.bottomVisible {
BottomSheetView(handler: handler, player: true)
}
}
but when I uncomment these lines:
if handler.bottomVisible {
BottomSheetView(handler: handler, player: true)
}
my .padding(.all,17) for VStack becomes damaged. Without this view, everything looks ok, but after adding the second child view for ZStack UI doesn't see paddings for VStack. Maybe I did smth wrong?
Example without bottom sheet:
and with the activated condition:

Well hard to answer your question because there is not so much code but you can try...
VStack(alignment: .leading) {
....
} .padding()
or you add the padding (.all, 17) information to all your textfields like
VStack{
Text("Hello Stackoverflow")
.padding(.all,17)
}

Related

Trouble With Constraints for a Navigation Link

Here is my code:
import SwiftUI
struct ContentView: View {
var link: some View {
NavigationLink(destination: OtherView()) {
Text("NLTitle")
}
.foregroundColor(.blue)
}
var body: some View {
NavigationView {
List {
ZStack {
HStack {
Text("1")
Spacer()
}.padding([.leading, .trailing], 20)
HStack {
Spacer()
Text("2")
.multilineTextAlignment(.center)
.frame(
alignment: .center
)
Spacer()
}
.padding([.leading, .trailing], 20)
HStack {
Spacer()
link
}.padding([.leading, .trailing], 20)
}
}
}
}
}
I have a NavigationLink (named 'link') in a list cell. I would like for the Text within 'link' to be to the rightmost side of the view. To try to accomplish this, I inserted 'link' in an HStack and put a Spacer() before it to try and push it to the rightmost part of the view. When I run the app though, the Text ends up in between Text("1") and Text("2") and I can't figure out why. I want Text("1") to be in the leftmost part of the view, Text("2") to be in the center of the view, and 'link' to be in the rightmost part of the view. I have provided visuals (the colors aren't important, I just wanted to make the different Texts clear):
Desired layout:
What I get instead:
I found that if I take everything out of the List view I get my desired layout. Also, if I keep everything in the List view and replace the NavigationLink with a Button I get my desired layout. The goal is to get the desired layout without having to change either of these aspects.
For the sake of clarity, I didn't include the code for OtherView() as I don't think it's necessary for this question.
The "quick" fix is to add fixedSize() to the NavigationLink
var link: some View {
NavigationLink(destination: Text("OtherView()")) {
Text("NLTitle")
}
.foregroundColor(.blue)
.fixedSize()
}
That will allow the link to shrink.

How to fix double back button in SwiftUI

I got 2 views. On the second view I have list of exercises and when I choose one of them and go inside I see double back. It's driving me crazy.
First one:
import SwiftUI
struct ProgrammView: View {
var body: some View {
NavigationView{
ScrollView(.vertical, showsIndicators: false) {
VStack {
Text("blabla")
.multilineTextAlignment(.center)
.font(.custom("AvenirNext-Bold", size: 30))
NavigationLink{
InsultHandProgram()
} label: {
Image("35")
.resizable()
.scaledToFit()
.padding(.horizontal)
.padding(.bottom, 7)
.shadow(radius: 5)
}
}
}
}
}
}
Second one:
import SwiftUI
struct InsultHandProgram: View {
let numbers = InsultProgram.getInsultProgram()
var body: some View {
NavigationStack {
List(numbers) { InsultProgram in
NavigationLink( InsultProgram.name, value: InsultProgram)
}
.navigationTitle("blabla")
.navigationDestination(for: InsultProgram.self) {
InsultProgram in InsultProgrammDetail(InsultProgram: InsultProgram)
}
}
}
I tried to change navigation stack. It's crushed.
If you use NavigationView, then it provides the navigation bars for all its child views. NavigationStack in your child view also wants to provide a navigation bar, and so you end up with two.
To remedy the situation you have some choices:
Remove the NavigationStack from your child view and let NavigationView manage everything.
Remove NavigationStack from you child view and replace NavigationView in your parent with a NavigationStack. This will work fine on iPhones, but doesn't adapt well to iPads.
Keep your navigation stack in the child view but replace NavigationView with NavigationSplitView. This came in with iOS16, as did NavigationStack. The two work well together so they don't step on each other's toes when it comes to setting up navigation bars.
Given you're already using other iOS 16 idioms such as navigationDestination I'd recommend approach 3.

In SwiftUI, how do I draw a view above a bottom toolbar?

I have a view with a bottom toolbar. When some user action is performed, I would like to display a drawer coming from the bottom that will cover this toolbar. I wasn't able to find a way to do this - the toolbar always remains on top. Here's an example of something I've tried:
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
VStack {
Spacer()
Text("Bottom Text")
}
.ignoresSafeArea(.all, edges: .bottom)
Text("Hello, world!")
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Text("Bottom Bar")
}
}
}
}
}
}
The "Bottom Text" label gets hidden behind the bottom toolbar.
The one thing I've tried so far is to remove the .toolbar modifier when the drawer is shown (by creating a custom view modifier that returns the .toolbar modifier conditionally). This works but seems like a hack, and also has some UI artifacts as the removal of the toolbar can shift some of the other view.
Any ideas how this can be solved?
Thanks!
To show content on top of the bottom toolbar, add your views as an overlay on the NavigationView itself:
var body: some View {
NavigationView {
Text("Hello, world!")
.toolbar {
ToolbarItemGroup(placement: .bottomBar) {
Button("Bottom Bar", action: {})
}
}
}
.overlay(
VStack {
Spacer()
Text("Bottom Text")
}
)
}
Original answer:
Remove the ignoresSafeArea. The bottom safe area includes the toolbar, so by ignoring it you’re telling Swiftui to put the content behind the toolbar.

Animated transitions and contents within ScrollView in SwiftUI

I'm not quite a SwiftUI veteran but I've shipped a couple of apps of moderate complexity. Still, I can't claim that I fully understand it and I'm hoping someone with deeper knowledge could shed some light on this issue:
I have some content that I want to toggle on and off, not unlike .sheet(), but I want more control over it. Here is some "reconstructed" code but it should be able capture the essence:
struct ContentView: View {
#State private var isShown = false
var body: some View {
GeometryReader { g in
VStack {
ZStack(alignment: .top) {
// This element "holds" the size
// while the content is hidden
Color.clear
// Content to be toggled
if self.isShown {
ScrollView {
Rectangle()
.aspectRatio(1, contentMode: .fit)
.frame(width: g.size.width) // This is a "work-around"
} // ScrollView
.transition(.move(edge: .bottom))
.animation(.easeOut)
}
} // ZStack
// Button to show / hide the content
Button(action: {
self.isShown.toggle()
}) {
Text(self.isShown ? "Hide" : "Show")
}
} // VStack
} // GeometryReader
}
}
What it does is, it toggles on and off some content block (represented here by a Rectangle within a ScrollView). When that happens, the content view in transitioned by moving in from the bottom with some animation. The opposite happens when the button is tapped again.
This particular piece of code works as intended but only because of this line:
.frame(width: g.size.width) // This is a "work-around"
Which, in turn, requires an extra GeometryReader, otherwise, the width of the content is animated, producing an unwanted effect (another "fix" I've discovered is using the .fixedSize() modifier but, to produce reasonable effects, it requires content that assumes its own width like Text)
My question to the wise is: is it possible to nicely transition in content encapsulated within a ScrollView without using such "fixes"? Alternatively, is there a more elegant fix for that?
A quick addition to the question following #Asperi's answer: contents should remain animatable.
You are my only hope,
–Baglan
Here is a solution (updated body w/o GeometryReader). Tested with Xcode 11.4 / iOS 13.4
var body: some View {
VStack {
ZStack(alignment: .top) {
// This element "holds" the size
// while the content is hidden
Color.clear
// Content to be toggled
if self.isShown {
ScrollView {
Rectangle()
.aspectRatio(1, contentMode: .fit)
.animation(nil) // << here !!
} // ScrollView
.transition(.move(edge: .bottom))
.animation(.easeOut)
}
} // ZStack
// Button to show / hide the content
Button(action: {
self.isShown.toggle()
}) {
Text(self.isShown ? "Hide" : "Show")
}
} // VStack
}

SwiftUI: Center Content in ScrollView

I'm trying to center a bunch of views in a VStack within a ScrollView in SwiftUI. To simplify things, I'm just trying to get it to work with a single Text view. Here's what I've come up with so far:
var body: some View {
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
This results in this:
I want the text to be in the middle like this:
So the HStack should be full-width and the Text should be centered within it. It seems like this should be easy, but I don't get what I'm doing wrong. :)
Using GeometryReader, you can get information about the size of the containing view and use that to size your view.
var body: some View {
GeometryReader { geometry in <--- Added
ScrollView(alwaysBounceVertical: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} //HStack
.frame(width: geometry.size.width) <--- Added
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
}
edit: after looking into this more, it seems that part of the problem is that you are using a ScrollView. If you remove that parent, the spacers in the HStack will automatically cause stretching to fill the view. I'm guessing the automatic stretching doesn't happen in ScrollViews because there's no finite limit to how big it can be, how much would it stretch? (because a ScrollView can scroll in any direction)
This seems to be a bug in Xcode 11.0 beta, ScrollView content wouldn't fill the scroll view. If you replace the ScrollView with a List it will work as expected. But if you have to use a scroll view, one workaround is to fix the scroll view's content width.
So your code will look something like this:
ScrollView(alwaysBounceVertical: true) {
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
Spacer()
} // HStack
.frame(width: UIScreen.main.bounds.width) // set a fixed width
.background(Color.green)
} // ScrollView
.background(Color.gray)
Result:
You should use the modifier frame and set its maxWidth: .infinity.
So it tells its parent: "the wider, the better" :)
var body: some View {
ScrollView(.vertical, showsIndicators: true){
HStack(alignment: .center) {
Spacer()
Text("This Is a Test")
.frame(maxWidth: .infinity) // <- this
Spacer()
} //HStack
.background(Color.green)
} //ScrollView
.background(Color.gray)
}
And this works regardless its parent.
Scrollview or whatever View it's set in.
Paul is doing a great job clarifying it to all of us here:
https://www.hackingwithswift.com/quick-start/swiftui/how-to-give-a-view-a-custom-frame
Answer compatible with Xcode 12.1 (12A7403)
I hope this helps 👍
dsa