Swiftui Vertically Misaligned Text

SwiftUI vertically misaligned text

This is happening because by default your HStack's vertical alignment is .center. I was able to achieve what you wanted by setting the HStack's vertical alignment to .firstTextBaseline and then adjusting the button's vertical alignment slightly.

    HStack(alignment: .firstTextBaseline) {
Button(action: { print("haha") }, label: {
Text("NL")
.foregroundColor(Color.purple)
})
.alignmentGuide(.firstTextBaseline, computeValue: { return $0[.bottom] + 1.5 })

TextField("00", text: $securityDigits)
.fixedSize()
TextField("BANK", text: $bankCode)
.fixedSize()
TextField("0000 0000 00", text: $accountNumber)
}

Here I'm saying the first text baseline of the button is the button's .bottom and then moving that baseline up ever so slightly. You may have to experiment with values but I found this to be the most visually correct for your use-case.

Sample Image

It seems to me there's a bug in Button though by virtue of it not properly advertising the first text baseline in the button itself. This could potentially be cleaner by making a .firstTextBaseline-aware Button "subclass" that you can reuse elsewhere instead of making micro-adjustments everywhere.

Vertically centering text in Swift UI

ScrollView has infinite inner space for its children. The VStack can't take all of this space. So VStack's height is defined by its content (in our case - Text).
Without ScrollView it will work like you want:

       var body: some View {
NavigationView {
VStack(alignment: .center) {
Text(String(format: "$%.2f", 0)).font(.largeTitle)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
.navigationBarTitle("Test")
}
}

Providing idealHeight for the VStack can be helpful as well. You can use GeometryReader to get the 'outer' height of the ScrollView:

        NavigationView {
GeometryReader { geometry in
ScrollView {
VStack(alignment: .center) {
Text(String(format: "$%.2f", 0)).font(.largeTitle)
}.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: geometry.size.height, maxHeight: .infinity)
.edgesIgnoringSafeArea(.all)
}.navigationBarTitle("Test")
}
}

Layout in SwiftUI with horizontal and vertical alignment

not an expert here, but I managed to achieve the desired layout by (1) opting for the 2-VStacks-in-a-HStack alternative, (2) framing the external labels, (3) freeing them from their default vertical expansion constraint by assigning their maxHeight = .infinity and (4) fixing the height of the HStack

struct ContentView: View {
@State var text = ""
let labels = ["Username", "Email", "Password"]

var body: some View {
HStack {
VStack(alignment: .leading) {
ForEach(labels, id: \.self) { label in
Text(label)
.frame(maxHeight: .infinity)
.padding(.bottom, 4)
}
}

VStack {
ForEach(labels, id: \.self) { label in
TextField(label, text: self.$text)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
}
.padding(.leading)
}
.padding(.horizontal)
.fixedSize(horizontal: false, vertical: true)
}
}

Here is the resulting preview:

Sample Image

in order to account for the misaligned baselines of the external and internal labels (a collateral issue that is not related to this specific layout – see for instance this discussion) I manually added the padding

credits to this website for enlightening me on the path to understanding
SwiftUI layout trickeries

SwiftUI Label text and image vertically misaligned

Probably a bug, so worth submitting feedback to Apple. Meanwhile here is working solution based on custom label style.

Tested with Xcode 12b

Sample Image

struct CenteredLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack {
configuration.icon
configuration.title
}
}
}

struct TestLabelMisalignment: View {
var body: some View {
Label("Play", systemImage: "play")
.labelStyle(CenteredLabelStyle())
}
}

SwiftUI change vertical alignment of Label to center image and text

We can do this using custom label style, like

struct DemoStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .center) { // << here !!
configuration.icon
configuration.title
}
}
}

and use it

Label {
Text("Test fdasf \n adfsa dsfsd f asdf \n asd fasd fads sad fda")
} icon: {
Image(systemName: true ? "checkmark.circle.fill" : "circle")
}
.labelStyle(DemoStyle())

demo

Tested with Xcode 13 / iOS 15

SwiftUI different alignment text in VStack

One of possibilities ... check it with Playground

There is not to much to explain, the "trick" is done with proper combination of different stack, alignment, .fixedSize(horizontal:, vertical:), Color.clear.frame(height:0) replacement of Spacer(). All together makes this "automatic" message view expansion, based on message text.

import SwiftUI
import PlaygroundSupport

struct ContentView: View {

var body: some View {

VStack {

HStack {
Spacer()
HStack {
VStack (alignment: .leading) {
Text("Lorem ipsum")
.font(.title)
.fixedSize()

Text("""
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
""")
.font(.system(size: 15))
.fixedSize(horizontal: false, vertical: true)

HStack {
Color.clear.frame(height: 0)
Text("22:13").fixedSize()
}
}
.padding()
.background(Color.yellow)
.cornerRadius(10)
.padding()

}
.scaledToFit()
}
.border(Color.red)

HStack {
Spacer()
HStack {
VStack (alignment: .leading) {
Text("Lorem ipsum")
.font(.title)
.fixedSize()

Text("?")
.font(.system(size: 15))
.fixedSize(horizontal: false, vertical: true)

HStack {
Color.clear.frame(height: 0)
Text("22:13").fixedSize()
}
}
.padding()
.background(Color.yellow)
.cornerRadius(10)
.padding()

}
.scaledToFit()
}
.border(Color.red)

HStack {
Spacer()
HStack {
VStack (alignment: .leading) {
Text("?")
.font(.title)
.fixedSize()

Text("Lorem ipsum")
.font(.system(size: 15))
.fixedSize(horizontal: false, vertical: true)

HStack {
Color.clear.frame(height: 0)
Text("22:13").fixedSize()
}
}
.padding()
.background(Color.yellow)
.cornerRadius(10)
.padding()

}
.scaledToFit()
}
.border(Color.red)

Spacer()
}
}
}

PlaygroundPage.current.setLiveView(ContentView())

RESULT:

Sample Image

THE SAME CODE IS REPEATED 3 TIMES, JUST BECAUSE I AM LAZY :-)

Finally you could use something like

struct Message<Header: View, Footer: View>: View {
let header: Header
let footer: Footer
let message: String
let color: Color

var body: some View {
HStack {
Spacer()
HStack {
VStack (alignment: .leading) {
header.fixedSize()

Text(message)
.fixedSize(horizontal: false, vertical: true)

HStack {
color.frame(height: 0)
footer.fixedSize()
}
}
.padding()
.background(color)
.cornerRadius(10)
.padding()

}
.scaledToFit()
}
}
}

or using @ViewBulder for header and footer

struct MessageBuilder<Header, Footer>: View where Header: View, Footer: View {

let header: () -> Header
let footer: () -> Footer
let message: String
let color: Color

init(@ViewBuilder header: @escaping () -> Header, @ViewBuilder footer: @escaping () -> Footer, message: String, color: Color) {
self.header = header
self.footer = footer
self.message = message
self.color = color
}

var body: some View {
HStack {
Spacer()
HStack {
VStack (alignment: .leading) {
header().fixedSize()

Text(message)
.fixedSize(horizontal: false, vertical: true)

HStack {
color.frame(height: 0)
footer().fixedSize()
}
}
.padding()
.background(color)
.cornerRadius(10)
.padding()

}
.scaledToFit()
}
}

}

and next use it in your code

struct ContentView: View {

var body: some View {

VStack {

Message(header: Text("Header").font(.title), footer: Text("22:13"), message: "long or short message text", color: Color.blue.opacity(0.2))

MessageBuilder(header: {
HStack {
Image(systemName: "square.and.arrow.down")
Text("Fred")
}
}, footer: {
Image(systemName: "clock")
}, message: "message text", color: Color.gray.opacity(0.2))

Spacer()
}
}
}

SwiftUI HStack vetrical Alignment doesn't work?

You should align the outer HStack instead.
Also the inner one is useless. So you can get rid of it.

VStack{
Spacer()
HStack(alignment: .top) {
Text("Error: \(errorText)")
Spacer()
Button("x"){ self.errorText = "" }
}
.frame(minHeight:10)
.padding(.all, 5)
}

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

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:

Screen shot of Text vs Button

Why is my SwiftUI VStack not aligning properly?

Change the innermost

VStack {

to

VStack(alignment: .leading) {

How to align text within a SwiftUI ZStack for smaller screen sizes?

I think your code is good, the problem is that your image is too wide and it's overflowing on the right side of the screen. The text label is aligning correctly to the trailing of the image (that's why it works on bigger devices), not to the trailing of the screen (what you want).

You should make sure your image doesn't overflow.

Image(item.mainImage)
.resizable()
.scaleToFit()


Related Topics



Leave a reply



Submit