Corner Radius of both HStack and TextField not updating - swift

This is the code for my content view. As you can see, I've tried both an HStack to contain the TextField, as well as just the TextField on its own. The corner radius doesn't have any bearing on the grey search rectangle - the edges are still perfectly rectangular.
#State var searchText = ""
var body: some View {
ZStack {
//function that's the content argument to ZStack
Color((.systemGreen))
VStack {
Text("App")
.font(.largeTitle)
.foregroundColor(Color.white)
//HStack {
TextField("Searchstring", text: $searchText)
.padding()
.background(Color(.systemGray6))
.padding()
.cornerRadius(12)
//}
// .padding()
// .background(Color(.systemGray6))
// .padding(.horizontal)
// .cornerRadius(12)
}
}
}
}
And this is what the preview looks like, in all cases:
corner radius fails to show in preview

The problem is here:
.padding() /// 1.
.background(Color(.systemGray6)) /// 2.
.padding() /// 3.
.cornerRadius(12) /// 4.
Your text field has a padding
background gets applied, after the padding
You add another padding, after the background
You apply another cornerRadius on top of padding
As a result, it's the padding that gets rounded, not the background.
Instead, you want to apply the cornerRadius immediately after the background.
struct ContentView: View {
#State var searchText = ""
var body: some View {
ZStack {
//function that's the content argument to ZStack
Color((.systemGreen))
VStack {
Text("App")
.font(.largeTitle)
.foregroundColor(Color.white)
TextField("Searchstring", text: $searchText)
.padding()
.background(Color(.systemGray6))
.cornerRadius(12) /// corner radius immediately after the background
.padding() /// extra padding outside the background
}
}
}
}
Result:

Related

Unexpected layout issues with SwiftUI Image and ZStack

I'm trying to create a view composed of two parts - the top part is an image with a piece of text overlaid in the top left corner. The top part should take up 2/3 of the height of the view. The bottom part is text information and a button, contained in an HStack, and takes up the remaining 1/3 of the view height.
struct MainView: View {
var body: some View {
GeometryReader { gr in
VStack(spacing: 0) {
ImageInfoView()
.frame(height: gr.size.height * 0.66)
BottomInfoView()
}
.clipShape(RoundedRectangle(cornerRadius: 20))
}
}
}
struct ImageInfoView: View {
var body: some View {
ZStack(alignment: .topLeading) {
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
HStack {
Text("Top left text")
.foregroundColor(.white)
.padding()
}
}
}
}
struct BottomInfoView: View {
var body: some View {
HStack {
VStack(alignment:.leading) {
Text("Some title")
Text("Some subtitle")
}
Spacer()
Button("BUTTON") {
}
}
.padding()
.background(.gray)
}
}
In the below sample code, if I set the frame height to 400 I will see the top left text, but when I set it to 200, I do not. What is happening here? The text should be anchored to the top left corner of ImageInfoView no matter what. Further, when set to 400, why does the ImageInfoView take up more than 66% of the height?
struct TestView: View {
var body: some View {
MainView()
.frame(height: 200)
.padding([.leading, .trailing], 16)
}
}
An image's fill pushes ZStack boundaries, so Text goes off screen (see this for details).
A possible solution is to put image into overlay of other container so it respects provided space instead of imposing own.
Tested with Xcode 13.4 / iOS 15.5
ZStack(alignment: .topLeading) {
Color.clear.overlay( // << here !!
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
)
// ... other code as-is
This fixes the problem:
ZStack(alignment: .topLeading) {
GeometryReader { gr in
Image("testImage")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: gr.size.width, maxHeight: gr.size.height)
.overlay(Rectangle()
.fill(Color.black.opacity(0.5))
)
HStack {
Text("Top left text")
.foregroundColor(.white)
.padding()
}
}
}
}

SwiftUI set blur on scrollview children except top most child

How would I set a blur on all of a scrollview's children (inside a vstack) except for the topmost visible child in the scrollview?
I've tried something like the following but it does not work.
ScrollViewReader { action in
ScrollView {
LazyVStack(alignment: .leading) {
ForEach(lyrics, id: \.self) { lyric in
Text(lyric)
.onAppear {
blurOn = 0.0
}
.onDisappear {
blurOn = 5.0
}
.padding()
.blur(radius: blurOn)
}
}
.font(.system(size: 20, weight: .medium, design: .rounded))
.padding(.bottom)
}
.cornerRadius(10)
.frame(height: 130)
}
After thinking about it, I guess I could put the VStack in a ZStack and place a 0 opacity view on the bottom with a blur...just wondering if there's a better way to do this?

Spacer not pushing view to top of screen inside ZStack?

How can I position my search-bar and button to the top of the screen with 65 padding from the top AND KEEP the onTapGestureFunctionality?
I tried the modifiers .edgesIgnoringSafeArea(.top) with padding(.top, 65) applied to the VStack inside my ZStack but then my onTapGesture would not work when tapping on either the search bar or the button...
Why does the Spacer() only push the elements about 80% to the top of the screen? Could this be because its inside a navigation view?
See image below.
Thank you
struct MapScreen: View {
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
VStack {
HStack(spacing: 15) {
SearchBar()
.padding(.leading, 5)
.onTapGesture {
print("search pressed")
}
ActivateClaimButton()
.onTapGesture {
print("claim pressed")
}
}
// 2 modifiers below positions correctly but
// onTapGesture doesn't work
// .padding(.top, 65)
// .edgesIgnoringSafeArea(.top)
Spacer()
}.zIndex(2)
MapView(locations: $locations, spotArray: $spotArray).edgesIgnoringSafeArea([.top, .bottom])
BottomNavBar().zIndex(1).edgesIgnoringSafeArea(.all).offset(y: 35)
}
.navigationViewStyle(StackNavigationViewStyle())
.fullScreenCover(isPresented: $presentSearchView, content: {
SearchView()
})
}
}
}
The answer that worked for me was
.navigationBarHidden(true)

VStack children to fill (not expand) its parent

i am trying to to make the button of an alert view fit the parent VStack. But I can only see two options:
button width as is, no frame modifier. that is not ideal as the button is not wide enough
set the frame modifier to .frame(maxWidth: .infinity). that is not ideal, because it not also fills its parent, but also makes it extend to the edges of the screen.
What I actually want is, that the VStack stays at its width and the button just fills up to the edges. No extending of the VStack. The size of the VStack is defined by the title and message, not by the button. Is this possible to achieve with SwiftUI?
Code:
Color.white
.overlay(
ZStack {
Color.black.opacity(0.4)
.edgesIgnoringSafeArea(.all)
VStack(spacing: 15) {
Text("Alert View")
.font(.headline)
Text("This is just a message in an alert")
Button("Okay", action: {})
.padding()
.frame(maxWidth: .infinity)
.background(Color.yellow)
}
.padding()
.background(Color.white)
}
)
As alluded to in the comments, if you want the width to be tied to the message size, you'll have to use a PreferenceKey to pass the value up the view hierarchy:
struct ContentView: View {
#State private var messageWidth: CGFloat = 0
var body: some View {
Color.white
.overlay(
ZStack {
Color.black.opacity(0.4)
.edgesIgnoringSafeArea(.all)
VStack(spacing: 15) {
Text("Alert View")
.font(.headline)
Text("This is just a message in an alert")
.background(GeometryReader {
Color.clear.preference(key: MessageWidthPreferenceKey.self,
value: $0.frame(in: .local).size.width)
})
Button("Okay", action: {})
.padding()
.frame(width: messageWidth)
.background(Color.yellow)
}
.padding()
.background(Color.white)
}
.onPreferenceChange(MessageWidthPreferenceKey.self) { pref in
self.messageWidth = pref
}
)
}
struct MessageWidthPreferenceKey : PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
}
I'd bet that there are scenarios where you would also want to set a minimum width (like if the alert message were one word long), so a real-world application of this would probably use max(minValue, messageWidth) or something like that to account for short messages.

How do I avoid app zoom when keyboard opens?

When I click my SwiftUI text field and the keyboard opens, the app zooms out (shown in video).
I have two questions about this behaviour:
Why does this happen?
How do I avoid this happening?
Here is my code:
struct BestillView: View { // This view is put inside a tab view with .ignoresSafeArea
#State var navn = ""
#State var varsling = true
var body: some View {
NavigationView {
ZStack {
Color("BackgroundColor")
.ignoresSafeArea()
VStack {
Image("Liquid") // This is my image overlayed on the background, i suspect this may be the only element that actually gets zoomed out
.resizable()
.aspectRatio(contentMode: .fit)
.ignoresSafeArea()
Spacer()
}
VStack {
ZStack(alignment: .leading) { // This is where the text field i'm having trouble with is
Color("UnselectedColor")
.frame(height: 50)
.cornerRadius(20.0)
if navn.isEmpty { // I have a separate text element as the placeholder text so i can give it a custom color
Text("Navn")
.foregroundColor(Color("AccentColor"))
.padding()
}
TextField("", text: $navn)
.padding()
}
.frame(width: 300)
Spacer()
.frame(height: 20.0)
// I removed the rest of my code, I don't think it should be necessary in this question - it's only a NavigationLink and a Toggle
}
}
}
}
}
You have .ignoresSafeArea() on your Image, but you actually need it on the VStack that contains the Image. The VStack is shrinking to fit the keyboard’s safe area, which squeezes the image too.
The view is actually not shrinking; the image is shrinking - because as the view moves up, it has less height to fit.
You can update your code as:
Image("Liquid")
.aspectRatio(contentMode: .fill)
and it will keep the size same - as the width will remain same.