I can't seem to get the Spacer() function to work when I'm within an HStack and trying to create space between my Text and Textfield views. The Spacer works to space out other areas of the view, but whenever I attempt to space between these two elements, it doesn't work.
Here is the code I'm working with:
VStack(alignment: .leading, spacing: 0) {
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
Text("Tickets")
.kerning(0.5)
.scaledFont(name: "Gotham Medium", size: 18)
.foregroundColor(Color("grey4"))
.padding(.horizontal)
.padding(.bottom, 3)
.padding(.top, 35)
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
HStack {
Text("Ticket URL")
.kerning(0.5)
.scaledFont(name: "Gotham Book", size: 16)
.foregroundColor(Color("spaceblack"))
Spacer()
TextField("Enter URL", text: $url)
}
.background(Color.blue)
.padding()
Rectangle()
.frame(height: 2, alignment: .top)
.foregroundColor(Color("grey2"))
}
.frame(maxWidth: .infinity)
.background(Color.red)
try adding .fixedSize() modifier to your TextField
TextField("Enter URL", text: $url)
.fixedSize()
or set a frame like so
TextField("Enter URL", text: $url)
.frame(width:200, height:50, alignment:.leading)
The problem is that TextField and Spacer() would take all available space and in this case TextField gets the priority; However, if you specify a fixed size or a frame to it then TextField won't stretch to take full space instead it will be fixed.
.fixedSize would allow your TextField to start small but eventually it will keep stretching the more text you write which can cause unwanted behavior.
.frame will fix your size to the provided width and hence there won't be any stretch and Spacer will have priority to take available space.
Related
I have a login screen in which I want to cover VStack whole white space which is remaining. I have 2 Vstack If I give Spacer to second one its not working.
My code
struct loginView: View {
#State private var stringOfTextField: String = String()
var body: some View {
ZStack {
LinearGradient(colors: [Color("primarycolor"), Color("secondarycolor")],
startPoint: .top,
endPoint: .center).ignoresSafeArea()
VStack() {
Text("Login").foregroundColor(.white).font(
.system(size: 18)
)
Image("logo")
Spacer()
}
VStack{
TextField("Enter Email", text: $stringOfTextField)
.padding()
.overlay(RoundedRectangle(cornerRadius: 10.0).strokeBorder(Color.gray, style: StrokeStyle(lineWidth: 1.0)))
.padding(.bottom)
TextField("Enter Password", text: $stringOfTextField)
.padding()
.overlay(RoundedRectangle(cornerRadius: 10.0).strokeBorder(Color.gray, style: StrokeStyle(lineWidth: 1.0)))
.padding(.bottom)
Text("Forgot Password ?")
.frame(maxWidth: .infinity, alignment: .trailing)
.padding(.bottom)
Button(action: {
print("sign up bin tapped")
}) {
Text("SIGN IN")
.frame(minWidth: 0, maxWidth: .infinity)
.font(.system(size: 18))
.padding()
.foregroundColor(.white)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color("secondarycolor"), lineWidth: 1)
)
}
.background(Color("secondarycolor"))
.cornerRadius(25)
}
.padding(.vertical, 60)
.padding(.horizontal, 20)
.background(Color.white)
.cornerRadius(30)
}
}
}
I try to add spacer in Vstack But its not working maybe due to Zstack. Its going on top if I give Spacer() to VStack
I think I understood what you are trying to achieve, just please next time post a complete minimal reproducible example, replacing the custom colours and images.
You can keep the logo visible and cover the bottom area with 4 changes:
Wrap your current VStack with a new one; you need this to add a Spacer() that will keep the logo visible.
Add a Spacer() at the top of the new VStack, right before the existing one
Add a Spacer() at the bottom of the existing VStack
Add the .ignoreSafeArea() modifier to the ZStack
Here's the code:
ZStack {
LinearGradient(colors: [.primary, .secondary],
startPoint: .top,
endPoint: .center).ignoresSafeArea()
VStack() {
Text("Login").foregroundColor(.white).font(
// ... rest of the code
}
// 1) New VStack around the first one
VStack {
// 2) Spacer() before the existing VStack with minimum length, to keep the logo visible
Spacer(minLength: 200)
VStack{
TextField("Enter Email", text: $stringOfTextField)
// ... rest of the code
.cornerRadius(25)
// 3) New Spacer() at the bottom of the existing VStack
Spacer()
}
.padding(.vertical, 60)
.padding(.horizontal, 20)
.background(Color.white)
.cornerRadius(30)
}
}
// 4) Add this modifier to the ZStack
.ignoresSafeArea(edges: [.bottom])
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:
I have two Text views. They are placed next to each other horizontally. How can I align them such that the meeting point of the views is also the center of the container view, regardless of how long either string is?
For example...
This is the first string|Second string.
The pipe here would be the center of the container view. Obviously a simple HStack wouldn't work unless both strings were exactly the same width. (Side note: In my particular use case, the strings won't be so long that they'll need to truncate or line wrap, but that might be useful for other people who have this question).
You can use a .frame(maxWidth: .infinity) on both Texts, which will end up making them equal widths (50% of the parent).
struct ContentView : View {
var body: some View {
VStack {
HStack(spacing: 2) {
Text("Short")
.frame(maxWidth: .infinity, alignment: .trailing)
.border(Color.blue)
Text("Longer. This is longer text....")
.lineLimit(1) // remove/change this if you want it to accommodate multiple lines
.frame(maxWidth: .infinity, alignment: .leading)
.border(Color.green)
}
}
}
}
You can play with the alignment on each depending on your need. Of course, the borders are only there for debugging.
To ensure both sides of the pipe are equal width, you can use 2 Spacer()s.
HStack {
Spacer()
Divider()
Spacer()
}
Then, you can overlay text on top of each Spacer().
struct ContentView: View {
var body: some View {
HStack {
Spacer()
.overlay(
Text("Hi! This text is in multiple lines. Cool, right?")
.fixedSize(horizontal: false, vertical: true) /// allow text wrap
.multilineTextAlignment(.trailing) /// align text to right when there are multiple lines
.frame(maxWidth: .infinity, alignment: .trailing) /// align text to right when there's 1 line
)
Divider()
Spacer()
.overlay(
Text("Hello!")
.fixedSize(horizontal: false, vertical: true)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
)
}
}
}
Result:
Given the following code snippet, I can align the leading edges of the text views but how can I specify that I want them to be aligned with their leading edges yet a certain number of pixels from the left edge of the actual VStack container?
VStack {
Text("Simple Swift Guide")
.font(.title)
Text("SwiftUI Tutorials")
.font(.subheadline)
.foregroundColor(.gray)
}
.frame(maxWidth: .infinity)
.background(Color.yellow)
Here is possible approach for your snippet (if I understood your goal correctly) - alignment is external so internal VStack content can be any.
VStack(alignment: .leading) {
Text("Simple Swift Guide")
.font(.title)
Text("SwiftUI Tutorials")
.font(.subheadline)
.foregroundColor(.gray)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 25)
.background(Color.yellow)
I'm trying to fit the TextEditor inside a ScrollView.
Is there a way to make TextEditor only takes up the space that it needs to fit all text?
or simply, how to change the height of the TextEditor dynamically to fit all the text?
You can put it in a ZStack with an invisible Text for sizing:
ZStack {
TextEditor(text: $text)
Text(text).opacity(0).padding(.all, 8) // <- This will solve the issue if it is in the same ZStack
}
using
TextEditor(text: someTextBinding)
.fixedSize(horizontal: false, vertical: true)
does the job for me (== height is dynamic, width is static)
Dynamic height can also be achieved with combination use of Text and overlay() with TextEditor.
Text(textVal.isEmpty ? "Your placeholder" : textVal)
.font(.body)
.padding(.vertical, 8)
.padding(.horizontal, 18)
.foregroundColor(Color.gray)
.opacity(textVal.isEmpty ? 1 : 0)
.frame(maxWidth: .infinity, minHeight: 40, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 5)
.stroke(Color.gray.opacity(0.2), lineWidth: 1)
.padding(.horizontal, 15)
)
.overlay(
TextEditor(text: $textVal)
.font(.body)
.foregroundColor(Color.black)
.multilineTextAlignment(.leading)
.padding(.horizontal, 15)
)
For transparent background of TextEditor add below code on View init:
init(yourProperty: String) {
self.yourProperty = yourProperty
UITextView.appearance().backgroundColor = .clear
}