SwiftUI: How to center align independent of other content in HStack? - swift

I have an HStack with content I want left aligned and then Details title to be centered. I am using a Spacer() to try and accomplish this but it looks like it's centering Details dependent on the content to the left of it. I would like it to be independent of that and just be centered to the screen. Is that possible using Spacer()?
VStack {
HStack(alignment: .top) {
Text("<------")
Spacer()
Text("Details")
Spacer()
}
Spacer()
}

A possible approach is to use overlay, like
VStack {
HStack(alignment: .top) {
Text("<------")
Spacer()
}
.overlay(
Text("Details") // << here !!
, alignment: .top)
Spacer()
}
, depending on needs you can place it to root VStack

Related

How to keep an item exactly in the center of an HStack with other items in the HStack

I have two items in an HStack. I want one of them to be perfectly horizontally centered and the other to be on the left side (have an alignment of .leading). Here is my code so far:
HStack {
Text("1")
.frame(alignment: .leading)
Text("Hello")
.frame(alignment: .center)
}
I have figured out that the .overlay method works. However, I still want Text("Hello") to be aware of the position of Text("1") so that if Text("Hello") were a longer string, it would know not to cover up Text("1"), which is exactly what happens when the .overlay function is used. So I'm wondering if there are any other solutions.
You can add one more Text view inside HStack as hidden which have the same content as your left align text view along with Spacer. Something like this.
HStack {
Text("1")
Spacer()
Text("Good Morning, Hello World. This is long message")
.multilineTextAlignment(.center)
Spacer()
Text("1") // Set the same content as left aligned text view
.hidden()
}
.padding(.horizontal)
Preview
Not sure if it's the best way, but you can use a GeometryReader to get the screen width and skip the first half.
To place the text exactly in the center, you should also calculate the width of the text. you can use a simple extension to get the size of your text:
extension String {
public func size(withFont font: UIFont) -> CGSize {
(self as NSString).size(withAttributes: [.font: font])
}
}
Here is the example code:
GeometryReader { geo in
HStack {
Spacer()
.frame(width: (geo.size.width - "center".size(withFont: UIFont.systemFont(ofSize: 18)).width) / 2)
HStack {
Text("center")
Text("another")
}
}
}

How to align the right side of an object to the exact middle of a view SwiftUI

I have an HStack with two Texts.
HStack {
Text("Hello")
Text("World")
}
I want Text("Hello") to be on the left side of the page and Text("World") to be on the right side of the page and for both texts to be equidistant from the exact horizontal center of the page. I added a visual for clarification:Visual
I've tried a bunch of different stuff but I haven't found any way to accomplish this task precisely.
You can use the alignment argument of the frame modifier.
Spacing argument for the HStack is for the spacing in between the texts.
HStack(spacing: 0) {
Text("Hello, World!")
.frame(maxWidth: .infinity, alignment: .trailing)
Text("Hello, World!")
.frame(maxWidth: .infinity, alignment: .leading)
}
I really didn't understand your need but according to your drawing I think you need something like below,
HStack{
Spacer()
Text("Hello")
.background(Color.orange)
Spacer().frame(width: 20)
Text("World")
.background(Color.orange)
Spacer()
}

How to make a clear SwiftUI view block the scrollview underneath it

So I have a ZStack that contains a ScrollView on the bottom and an HStack that's aligned at the top. It looks something like this:
ZStack {
ScrollView {
//Content
}
VStack {
HStack {
Spacer()
Circle()
.frame(width: 30, height: 30)
Spacer()
}
Spacer()
}
}
Now, I want that HStack to block any interaction with the ScrollView beneath it. I've noticed that when I set the background on the HStack to a non-clear color, it behaves as I'd like it to. However, if the background is clear, then the touches go through the HStack and interact with the ScrollView beneath it. I've also tried using .allowsHitTesting(true) on the HStack with no luck.
Any help is much appreciated!
https://i.stack.imgur.com/5Jzby.png
"...I've noticed that when I set the background on the HStack to a non-clear color, it behaves as I'd like it to.", then you could use Color.white.opacity(0.001)
instead of Color.clear or non-clear color
You can easily accomplish this by putting it in a VStack instead of a ZStack. The touches will be ignored as it's above the ScrollView. You can also have a look at .layoutPriority(), it may also be useful. Documentation is here.
struct ContentView: View {
var body: some View {
VStack {
HStack {
Spacer()
Circle()
.foregroundColor(Color.blue)
.frame(width: 30, height: 30)
Spacer()
}
.padding()
// or this does the same thing as the HStack two spacers
/*
Circle()
.foregroundColor(Color.blue)
.frame(width: 30, height: 30)
.frame(minWidth: 0, maxWidth: .infinity)
*/
Spacer()
ScrollView {
ForEach(1..<11) { value in
Text("\(value)")
}
}
}
}
}

Align heights of HStack members

I want to align the heights of two HStack members. The expected outcome would be that the image has the same size as the text. The current outcome is that the image has more height than the text. This is my current setup:
HStack {
Text("Sample")
.font(.largeTitle)
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
)
Spacer()
Image(systemName: "checkmark.seal.fill")
.resizable()
.scaledToFit()
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
)
}
What I've tried:
.fixedSize() -> I tried to tack this modifier onto the Image but the result was that the Image's height got smaller than the text's. Probably because the SFSymbol's intrinsic height is smaller than the .largeTitle intrinsic height.
AlignmentGuide -> I tried to create a custom alignment guide where I initially thought I could say "align bottom of Image and Text and align top of Image and Text" and therefore have the same height. But it seemed like you can only apply a single alignment guide per stack view.
GeometryReader -> I tried to wrap the HStack in a GeometryReader in which I tacked the .frame(height: proxy.frame.height) view modifier on the Text and Image. This also did not help because it somehow just made some white space around the views.
How it is:
How I want it:
Wrap your Image in a Text. Since your image is from SF Symbols, SwiftUI will scale it to match the dynamic type size. (I'm not sure how it will scale other images.)
VStack {
let background = RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
ForEach(Font.TextStyle.allCases, id: \.self) { style in
HStack {
Text("\(style)" as String)
.padding()
.background(background)
Spacer()
Text(Image(systemName: "checkmark.seal.fill"))
.padding()
.background(background)
}
.font(.system(style))
}
}
You can get the size of the Image small by adding a .frame() modifier to your HStack. See the code below,
HStack {
// Some Content
}
.frame(height: 60) // Interchangeable with frame(maxHeight: 60)
The Result:
For your exact example, I found 60 to be the sweet spot. But if you wanted a more dynamic solution, I'd make a few changes to your code. See the code below.
HStack {
Text("Sample")
.font(.largeTitle)
.frame(maxHeight: .infinity) // force the text to take whatever height given to the Parent View, which is the HStack
.padding(.horizontal) // Add padding to the Text to the horizontal axis
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
)
Spacer()
Image(systemName: "checkmark.seal.fill")
.resizable()
.scaledToFit()
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.foregroundColor(.red)
)
}
.background(Color.gray)
.frame(height: 100) // Change this value and the embedded Views will fit dynamically
The output will work as shown in the GIF below,
Here is an upgrade version of rob answer which support Assets Image plus system Image! Almost any Image! Like this:
struct ContentView: View {
var body: some View {
VStack {
ForEach(Font.TextStyle.allCases, id: \.self) { style in
HStack {
Text(String(describing: style))
.padding()
.background(Color.pink.opacity(0.5).cornerRadius(10.0))
Spacer()
}
.font(.system(style))
.background(
Image("swiftPunk")
.resizable()
.scaledToFit()
.padding()
.background(Color.yellow.cornerRadius(10.0))
, alignment: .trailing)
}
}
}
}
Result:

How to move this to the top?

I just learned how to implement specific rounded corners, but now it seems nothing will align to the top of the screen, even with spacers. How can I get it to align to the top of the screen?
Additionally, I would like the green to ignore the top safe area, but it wasn't doing that earlier either.
import SwiftUI
struct Dashboard: View {
#State var bottomLeft: CGFloat = 25
var body: some View {
ZStack {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text("Good Morning, Sarah")
.font(Font.system(size: 36))
.foregroundColor(.standaloneLabelColor)
.fontWeight(.semibold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
Text("We're glad to see you and hope you're doing well. Let's take on the day.")
.font(Font.system(size: 20))
.foregroundColor(.standaloneLabelColor)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.bottom)
}
.background(Color.appColor)
.cornerRadius(bottomLeft, corners: .bottomLeft)
.padding(.bottom, 10)
}
Spacer()
}
}
}
Thanks in advance.
You can set the alignment on the ZStack to .top. You can then also remove the Spacer.
A Spacer does nothing in a ZStack - since it's on its own layer. But setting the alignment on the ZStack will align all views to the top. If this is not what you want, you can also just put the Spacer in the inner VStack and the contents of that will all be aligned to the top also.
Code:
ZStack(alignment: .top) {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
/* ... */
}
}
Also, to ignore the safe area at the top when applying the rounded corners, you should clip the corners and use ignoresSafeArea() within background. This ensures that you are only ignoring the safe area for the background, and not the whole view. Do the following:
.background(
Color.appColor
.cornerRadius(bottomLeft, corners: .bottomLeft)
.ignoresSafeArea()
)
// .background(Color.appColor)
// .cornerRadius(bottomLeft, corners: .bottomLeft)
.padding(.bottom, 10)
I had to remove some bits from your code as they were producing errors on my end. Working with what I had, you needed to add a Spacer() below the appropriate VStack.
Since your content was stored in a VStack embedded in another VStack, the outer VStack was essentially where the entire view lived. Putting a Spacer beneath this pushes it up to the top of the screen.
You can additionally add padding to the top of the VStack to move the view lower if you do not want it touching the top of the screen.
Code below:
import SwiftUI
struct ContentView: View {
#State var bottomLeft: CGFloat = 25
var body: some View {
ZStack {
Color("background")
.edgesIgnoringSafeArea(.all)
VStack {
VStack {
Text("Good Morning, Sarah")
.font(Font.system(size: 36))
.foregroundColor(Color.blue)
.fontWeight(.semibold)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
Text("We're glad to see you and hope you're doing well. Let's take on the day.")
.font(Font.system(size: 20))
.foregroundColor(Color.blue)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.bottom)
}
.background(Color.green)
.cornerRadius(bottomLeft)
.padding(.bottom, 10)
Spacer() //Added a spacer here
}
}
}
}