SwiftUI: How to restrict view to only resizing in one direction? - swiftui

How can I restrict the view to resize only downwards?
I am trying to add more buttons & have the view resize to accommodate the buttons without overlapping the red rectangle above.
This is the code I am currently using:
struct ContentView: View {
var body: some View {
VStack{
RoundedRectangle(cornerRadius: 20)
.frame(width: .infinity, height: 55)
.foregroundColor(.red)
HStack{
Spacer()
ZStack{
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.green)
VStack{
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
Button(action: {}) {
Text("Test Button")
.foregroundColor(.white)
.font(.headline)
.frame(width: .infinity)
Spacer()
}.padding()
}
}.frame(width: 250, height: 100)
}
Spacer()
}
}
}

Change your .frame(width: 250, height: 100) to .frame(width: 250, height: 100, alignment: .top) Your original example defaults to .center, which is why it expands into the red rectangle.

Related

SwiftUI: Text mis-alignment when inside Button (bug?)

So this is a weird one, and it looks like a bug. When placed inside a button, title that cannot fit on one line mis-aligns. It should be aligned to the left, but shows up centered.
Everything latest, XCode 13.2.1
MRE is below, any tips much appreciated!
HStack {
HStack{
Image(systemName: "square.and.pencil")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
Text("Some kind of title") // <- aligned properly
.fontWeight(.bold)
.minimumScaleFactor(0.2)
} //: HStack
.padding(.horizontal, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 35)
.font(.headline)
.foregroundColor(Color.green)
.background(Color.green.opacity(0.2))
.cornerRadius(6)
Button(action: {
// some action
}){
HStack{
Image(systemName: "square.and.pencil")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
Text("Some kind of title") // <- not aligned properly
.fontWeight(.bold)
.minimumScaleFactor(0.2)
} //: HStack
.padding(.horizontal, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 35)
.font(.headline)
.foregroundColor(Color.green)
.background(Color.green.opacity(0.2))
.cornerRadius(6)
} //: Button
} //: HStack
You can fix it by adding the multilineTextAlignment modifier to the Text view:
Button(action: {
// some action
}){
HStack{
Image(systemName: "square.and.pencil")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
Text("Some kind of title")
.fontWeight(.bold)
.minimumScaleFactor(0.2)
.multilineTextAlignment(.leading) // <- will align to the left
} //: HStack
.padding(.horizontal, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.frame(height: 35)
.font(.headline)
.foregroundColor(Color.green)
.background(Color.green.opacity(0.2))
.cornerRadius(6)
} //: Button
As to why it happens, I have a theory. By default Text on buttons is centered, while plain Text by default is left-aligned. I didn't see it explicitly mentioned in the documentation, but a simple experiment shows this behavior:

How fix Stacks to top and bottom using SwiftUI

how are you ?
I need to fit all the content of first zstack (I use two zstack to add a transparent layer over first zstack background) to window size and if the user scroll down, shows additional stacks added below.
I add Spacer() to push the last HStack to bottom and before stacks to the top (in middle must be a dynamic blank space) of window without result.
Can help me please ?
Thanks
ScrollView(.vertical) {
ZStack {
bgColors[weatherDescription]
ZStack {
LinearGradient(gradient: Gradient(colors: [Color(hex: 0x212121, alpha: 0.5), Color(hex: 0x212121, alpha: 0.5)]), startPoint: .top, endPoint: .bottom)
VStack(alignment: .leading) {
Text("Hello!")
.foregroundColor(Color.white)
.fontWeight(.heavy)
.font(.largeTitle)
.padding(.top, 20)
.padding(.bottom, 1)
Text("Hello!")
.foregroundColor(Color.white)
.font(.title)
.padding(.bottom, 1)
Text("Hello!")
.foregroundColor(Color.white)
.font(.title3)
.padding(.bottom, 20)
HStack(alignment: .center, content: {
Text("TEXT1")
.foregroundColor(Color.white)
.font(.title3).fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .leading)
Text("TEXT2")
.foregroundColor(Color.white)
.font(.title3).fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .trailing)
}).frame(maxWidth: .infinity)
Spacer()
HStack(alignment: .center, spacing: 30, content: {
Text("Text1")
.foregroundColor(Color.white)
.font(.system(size: 90))
Text("Text2")
.foregroundColor(Color.white)
.font(.caption)
.animation(.easeIn)
}).frame(width: .infinity, alignment: .bottom)
}.padding() // vstack
}.edgesIgnoringSafeArea(.top) // zstack
}.edgesIgnoringSafeArea(.top) // zstack
// ADITIONALS STACKS VISIBLES ONLY WHEN SCROLL DOWN
}
You just used so many codes, I edited them to less code, here for example you just used so many foregroundColor which you can see in my code I used just one time on parent View.
Also we can not use ScrollView like you used it! ScrollView needs Content to Scroll, you have less Content.
import SwiftUI
struct ContentView: View {
var body: some View {
ZStack {
LinearGradient(gradient: Gradient(colors: [Color.red, Color.purple]), startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack(alignment: .leading, spacing: 30) {
Text("Hello!")
.fontWeight(.heavy)
.font(.largeTitle)
Text("Hello!")
.font(.title)
Text("Hello!")
.font(.title3)
HStack {
Text("TEXT1")
.font(.title3)
.fontWeight(.bold)
Spacer()
Text("TEXT2")
.font(.title3)
.fontWeight(.bold)
}
Spacer()
HStack {
Text("Text1")
.font(.system(size: 90))
Spacer()
Text("Text2")
.font(.caption)
}
}
.padding()
}
.foregroundColor(Color.white)
.animation(.easeIn)
}
}

Textfield in SwiftUI doesn't work, I can't type in it anymore

I have made a login view in SwiftUI and it worked at first but now that I have styled the view, my textfields don't work anymore. I can't type anything in the Textfields. Is this a Xcode bug or have I made a mistake. This is the code:
ZStack{
Color.red.ignoresSafeArea()
VStack {
Image("login")
.resizable()
.frame(width: 150, height: 150)
.shadow(radius: 5)
.padding(.top, 20)
.offset(y: -50)
Spacer()
}
VStack {
Spacer()
VStack {
HStack {
Image(systemName: "mail")
TextField("Email", text: $email)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
.padding(.top, 30)
HStack {
Image(systemName: "lock.rectangle")
SecureField("Password", text: $password)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
.zIndex(1)
Button(action: {login()}) {
Text("Sign in")
.padding()
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width - 100, height: 50)
.background(Color.red)
.cornerRadius(10)
.padding(.horizontal)
.padding(.top)
}.buttonStyle(PlainButtonStyle())
NavigationLink(destination: Register()) {
Text("No account? Sign up!")
.padding()
.foregroundColor(.black)
.cornerRadius(35)
.padding(.horizontal)
}.buttonStyle(PlainButtonStyle())
Spacer()
}
.frame(height: UIScreen.main.bounds.height * 0.6)
.background(RoundedCorners(color: .white, tl: 30, tr: 30, bl: 0, br: 0))
.ignoresSafeArea()
.offset(y: 35)
}.shadow(radius: 15)
}
The login button and register text do work so I don't think that there is an invisible object above the textfields.
This is my design
First, you are using three views insid the ZStack. In other words you are laying three layers over the others. You should use 2 views inside the Zstack.
Second, use the clipped() modifier before shadow().
ZStack{
Color.red
VStack {
Spacer()
Image(systemName: "plus")
.resizable()
.frame(width: 150, height: 150)
.shadow(radius: 5)
.padding(.top, 20)
.offset(y: -50)
VStack {
HStack {
Image(systemName: "mail")
TextField("Email", text: $email)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
.padding(.top, 30)
HStack {
Image(systemName: "lock.rectangle")
SecureField("Password", text: $password)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
Button(action: {}) {
Text("Sign in")
.padding()
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width - 100, height: 50)
.background(Color.red)
.cornerRadius(10)
.padding(.horizontal)
.padding(.top)
}.buttonStyle(PlainButtonStyle())
NavigationLink(destination: Text("hello")) {
Text("No account? Sign up!")
.padding()
.foregroundColor(.black)
.cornerRadius(35)
.padding(.horizontal)
}
.buttonStyle(PlainButtonStyle())
Spacer()
}
.frame(height: UIScreen.main.bounds.height * 0.6)
.background(Color.white)
.clipped()
.shadow(radius: 15, x: 0, y: 0)
}
}
.ignoresSafeArea()
However, using the ZStack is unnecessary.
VStack {
Spacer()
Image(systemName: "plus")
.resizable()
.frame(width: 150, height: 150)
.shadow(radius: 5)
.padding(.top, 20)
.offset(y: -50)
VStack {
HStack {
Image(systemName: "mail")
TextField("Email", text: $email)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
.padding(.top, 30)
HStack {
Image(systemName: "lock.rectangle")
SecureField("Password", text: $password)
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
.padding(.horizontal)
Button(action: {}) {
Text("Sign in")
.padding()
.foregroundColor(.white)
.frame(width: UIScreen.main.bounds.width - 100, height: 50)
.background(Color.red)
.cornerRadius(10)
.padding(.horizontal)
.padding(.top)
}.buttonStyle(PlainButtonStyle())
NavigationLink(destination: Text("hello")) {
Text("No account? Sign up!")
.padding()
.foregroundColor(.black)
.cornerRadius(35)
.padding(.horizontal)
}
.buttonStyle(PlainButtonStyle())
Spacer()
}
.frame(height: UIScreen.main.bounds.height * 0.6)
.background(Color.white)
.clipped()
.shadow(radius: 15, x: 0, y: 0)
}
.background(Color.red)
.ignoresSafeArea()
You should use .background(RoundedCorners(color: .white, tl: 30, tr: 30, bl: 0, br: 0)) adjust the offset of the innner Vstack.
This has happened to me before. Usually it has just been an Xcode glitch and I have to quit and restart the IDE a couple times before it actually shows up. However in Simulator it works. Hope it's resolved now though! :)

ScrollView text alignment SwiftUI

I have a vertical ScrollView with a ForEach loop that has HStack's inside with Text, Image, and Two Text's. I'd like to keep each row's items in alignment with each other. I've attempted to wrap in a VStack with alignment .leading but did nothing. The issue I'm having is the text doesn't line up. Still very new to SwiftUI so any help would be appreciated.
ScrollView(.vertical) {
ForEach(self.daily.forecast, id: \.date) { forecast in
HStack (spacing : 10){
Text(forecast.date ?? "")
.fontWeight(.bold)
Spacer()
RequestImage(Url("IMAGE_URL"))
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.clipped()
Spacer()
Text("\(forecast.maxTemp)")
.fontWeight(.bold)
Text("\(forecast.minTemp)")
.fontWeight(.bold)
}
}
}
.padding(.all, 15)
.onAppear() {
authorize {_ in
loadForecast()
}
}
UPDATED:
I was able to get a bit closer but things still aren't aligned to where I'd like them to be.
ScrollView(.vertical) {
ForEach(self.daily.forecast, id: \.date) { forecast in
HStack (spacing : 10){
Text(date)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 45)
Spacer()
RequestImage(Url("IMAGE_URL"))
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.frame(minWidth: 50)
.clipped()
Spacer()
Text(maxTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 45)
Text(minTemp)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(Color.secondary)
.frame(minWidth: 45)
}
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
}
}
.padding(.all, 15)
.onAppear() {
authorize {_ in
loadForecast()
}
}
Here is a demo of possible solution. Prepared with Xcode 12.4 / iOS 14.4
struct DemoWeatherLayout: View {
private var dayOfTheWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sut"]
var body: some View {
ScrollView {
VStack {
ForEach(dayOfTheWeek, id: \.self) {
WeatherRowView(day: $0,
icon: ["sun.max", "cloud.sun", "cloud", "cloud.snow"].randomElement()!,
low: Array(-5..<0).randomElement()!,
high: Array(1..<15).randomElement()!)
}
}.padding()
}
}
}
struct WeatherRowView: View {
var day: String
var icon: String
var low: Int
var high: Int
var body: some View {
HStack {
Text(day)
.frame(maxWidth: .infinity, alignment: .leading)
Image(systemName: icon)
.frame(maxWidth: .infinity, alignment: .leading)
HStack {
Text("+XXº").foregroundColor(.clear)
.overlay(Text(String(format: "%+d", low)), alignment: .leading)
Text("+XXº").foregroundColor(.clear)
.overlay(Text(String(format: "%+d", high)), alignment: .leading)
}
}
}
}
Three suggestions:
add alignment parameter to HStack:
HStack(alignment: .lastTextBaseline, spacing: 10) { ... //others options: .top, .center, .firstTextBaseline...
Add minLength parameter to Spacer:
Spacer(minLength: 0) but with spacing: 10 in HStack it is a bit redundant. You can remove spacing: 10 for Spacer(minLength: 10)
Add a Spacer(minLength: 0) at the end after Text("\(forecast.minTemp)").fontWeight(.bold)
I'm going to accept #Asperi answer as correct. The UI has indeed changed since to better fit said labels etc. Appreciate everyone's else. The code is very much ugly but works. Not sure if it's 100% correct.
HStack (spacing: 10){
Text(date)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
Spacer()
RequestImage(Url("IMAGE_URL_HERE"))
.aspectRatio(contentMode: .fit)
.frame(width: 35, height: 35)
.frame(minWidth: 55)
.clipped()
Spacer()
Text(maxTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 50)
.background(Color.orange)
.cornerRadius(5)
Text(minTemp)
.font(.title3)
.fontWeight(.bold)
.frame(minWidth: 50)
.background(Color.blue)
.cornerRadius(5)
}

Button with image for SwiftUI 2

I want to add button with image inside of the view like twitter button with image for SwiftUI 2, but it is not padding to any directions.
struct WeatherView: View {
var body: some View {
VStack(alignment: .leading, spacing:0){
Button(action: {}) {
Image("cloud")
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.clipShape(Circle())
.shadow(radius: 8)
}
HStack(spacing: 5){
Text("Rainy")
.font(.largeTitle)
.fontWeight(.medium)
Spacer()
Button(action: {
}) {
Image("menu").resizable().frame(width: 24, height: 24)
}
}
.foregroundColor(Color("black"))
.padding()
Spacer()
}
}
}
Is there any solution for this problem.
I hope you are doing well.
This code should do the trick.
VStack {
Spacer()
Button(action: {}) {
Image(systemName: "cloud.fill")
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.clipShape(Circle())
.shadow(radius: 8)
}
}
.frame(maxWidth: .infinity, alignment: .trailing)
.padding()
By putting the button in a VStack, and putting a spacer before it, you will push the button to the bottom of the screen. Then by putting a frame on it, and making the width to be .infinity, and giving it alignment trailing, you will further push the button to the trailing side of the screen, which in this case results in making the button at the bottom right corner.
Edit: Included full code
struct WeatherView: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
HStack(spacing: 5) {
Text("Rainy")
.foregroundColor(.black)
.font(.largeTitle)
.fontWeight(.medium)
Spacer()
Button(action: {}) {
Image("menu").resizable().frame(width: 24, height: 24)
}
}
.foregroundColor(Color("black"))
.padding()
VStack {
Spacer()
Button(action: {}) {
Image("cloud")
.padding()
.background(Color.blue)
.foregroundColor(Color.white)
.clipShape(Circle())
.shadow(radius: 8)
}
}
.frame(maxWidth: .infinity, alignment: .trailing)
.padding()
}
}
}