Swift UI VStack align up. Make textfield appear with keyboard when pressing in it - alignment

I am having issues placing my "twitter" image higher in my view while using SwiftUI. I want to get the twitter image higher up for all closer to my two blob images, as well as add the spacing between the twitter image and the "Sign into my account" phrase. In addition I am trying to get my UITextfield, I guess now its just TextField to appear with the keyboard when clicked in below my "Sign into your account phrase" but this is now showing up in my view.
Please see image of what is happening attached.
GeometryReader { (deviceSize: GeometryProxy) in
ZStack {
//Define a screen color
LinearGradient (gradient: Gradient(colors:[Color(ColorsSaved.gitLabDark),Color(ColorsSaved.gitLabLight)]),startPoint: .leading,endPoint: .trailing)
//Extend the screen to all edges
.edgesIgnoringSafeArea(.all)
Image("blobVectorLight")
.resizable()
.frame(width: deviceSize.size.height * (220/812) * 0.81, height: deviceSize.size.height * (220/812), alignment: .topTrailing)
.frame(width: deviceSize.size.width, height:deviceSize.size.height , alignment: .topTrailing)
.edgesIgnoringSafeArea(.all)
Image("blobVectorDark")
.resizable()
.frame(width: deviceSize.size.height * (208/812) * 0.74, height: deviceSize.size.height * (208/812), alignment: .topTrailing)
// .overlay(Image("blobVectorLight"))
.frame(width: deviceSize.size.width, height:deviceSize.size.height , alignment: .topTrailing)
.edgesIgnoringSafeArea(.all)
VStack{
Image ("twitter")
.resizable()
.frame(width: deviceSize.size.height*(77/812)*2.078, height: deviceSize.size.height*(77/812))
Text ("Sign into your account")
.foregroundColor(.white)
TextField ("Username")
}
}
}
}
}

To move any element up use:
.offset(y: -200.0)
To increase spacing between elements in a VStack declare it like this:
VStack(spacing: 20.0) { //....
Too use a TextField you need a value to bind it to:
//outside the body declare:
#State private var username: String = ""
//and then inside the body:
TextField("Username", text: $username)

I just sum up all what you are supposed to do here:
VStack(spacing: 50){
Spacer()
Image ("image")
.resizable()
.frame(width: deviceSize.size.height*(77/812)*2.078, height: deviceSize.size.height*(77/812))
Spacer()
Text ("Sign into your account")
.foregroundColor(.white)
TextField.init("Username", text: self.$userName)
Spacer()
}
above the body:
#State var userName: String = ""
var body: some View {
......
Hope you get what you want.

Related

SwiftUI Stack fill remaining height

I am a beginner in Swift and in iOS development. I am currently trying to create a sing in form with linear background on the top.
I've placed background view to gradient variable. Now I am trying to place my form after background on the top. But it doesn't fill the remaining space. I've tried using Spacer, but then form aligns to the bottom of the screen. How can I expand and align VStack with the form right after the gradient and make it fill the remaining height of the screen.
Here is my code
var gradient: some View {
ZStack(alignment: .top) {
LinearGradient(
stops: [
.init(color: .red, location: 0.4),
.init(color: .orange, location: 0.7),
.init(color: .yellow, location: 0.8)
],
startPoint: .leading,
endPoint: .trailing)
.frame(maxWidth: .infinity, maxHeight: 300, alignment: .top)
.mask(
RoundedRectangle(cornerRadius: 96)
.fill(Color.black)
.frame(width: .infinity, height: .infinity)
)
.blur(radius: 64)
}
.ignoresSafeArea()
.frame(height: .infinity, alignment: .top)
.offset(y: -100)
}
var body: some View {
VStack() {
gradient
Spacer()
VStack () {
VStack (spacing: 8) {
Text("Sign In")
.font(.title)
.fontWeight(.bold)
Text("Access to your account")
.font(.callout)
.foregroundColor(.secondary)
}
VStack (spacing: 18) {
TextField("Username", text: $username)
.modifier(TextFieldOutlined())
TextField("Password", text: $password)
.modifier(TextFieldOutlined())
}
.padding()
}
}
.frame(height: .infinity)
.padding(.bottom, 48)
.ignoresSafeArea()
}
Now it looks like this. With form Stack selected. It don't know why it is not filling the remaining height.
If I understand what you're looking for, you'd like the top portion of the screen to be a fixed size for the gradient, and then the form centered in the remaining part of the screen. You could do this by making the VStack expand using Spacers, modifying the body code to:
var body: some View {
VStack() {
gradient
VStack () {
// Add spacing above the form
Spacer()
VStack (spacing: 8) {
Text("Sign In")
.font(.title)
.fontWeight(.bold)
Text("Access to your account")
.font(.callout)
.foregroundColor(.secondary)
}
VStack (spacing: 18) {
TextField("Username", text: $username)
.modifier(TextFieldOutlined())
TextField("Password", text: $password)
.modifier(TextFieldOutlined())
}
.padding()
// Add spacing below the form
Spacer()
}
}
.padding(.bottom, 48)
.ignoresSafeArea()
}
Some of the padding, as currently written, will keep the form from being perfectly vertically centered, so you may want to tweak that.
Without your TextFieldOutlined() modifier, it looks like this in my preview (gradient is selected and outlined for emphasis):

SwiftUI Tap Area Below Button

I have a View in my app that shows an Image as the background (via Binding) and then it has a button to download the image and another to favorite the image.
The problem is that when I use the .ignoreSafeArea on a whole ZStack, the tap area on the button shifts to below the button body. I have the images scaled to fit and clipped and I tried moving the buttons down with padding, and I have tried seeing if there was an alternative to making the whole ZStack ignore the safe area with no luck.
I should also mention that I have put the image as the background with the .background property, I tried just putting it at the top of a ZStack but that would also affect the buttons positions.
I should also mention I am relatively new to Swift
struct photoDetail: View {
let image: realPhoto
#State var textControl: Bool
init(image: realPhoto) {
self.image = image
self.textControl = image.favorited
}
var body: some View {
ZStack {
VStack(alignment: .leading) {
Spacer()
HStack {
Text("created by: \(image.username)")
.padding(5)
.foregroundColor(.white)
.background(Color(.gray).opacity(0.8))
.padding(.leading, 20)
.font(.system(size:20, weight: .semibold))
.padding(.bottom, 50)
.cornerRadius(4)
Spacer()
}.padding(.bottom, 60)
}
VStack {
HStack {
Spacer()
Button {
let imageSaver = ImageSaver()
imageSaver.writeToPhotoAlbum(image: UIImage(data: image.image)!)
} label: {
Image(systemName: "square.and.arrow.down")
.resizable()
.scaledToFit()
.frame(width: 20)
.padding(8)
}
Button {
favoriteToggle()
} label: {
Image(systemName: textControl == false || image.favorited == false ? "heart": "heart.fill")
.resizable()
.scaledToFit()
.frame(width: 25)
.padding(8)
}.padding(.trailing, 30)
}.padding(.top, 100)
Spacer()
}
}.onAppear() {
self.textControl = image.favorited
}.background(
Image(uiImage: UIImage(data: image.image)!)
.resizable()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.scaledToFill()
.clipped()
).ignoresSafeArea()
}
}
Try to add .contentShape(Rectangle()) to your buttons. It should limit tappable area to your button view

SwiftUI combine views with Stack

I am trying to create a design like this using text and image in a ZStack but my current implementation isn't showing the text at all, and I don't know why
ZStack {
Text("15")
.frame(alignment: .topTrailing)
.cornerRadius(10)
Image(systemName: agree ? "hand.thumbsup" : "hand.thumbsdown")
.frame(width: 40, height: 40)
.foregroundColor(agree ? .green : .red)
.background(.gray)
.cornerRadius(8)
}
In a ZStack, the views are stacked on the Z axis with the first defined views at the "bottom" of the stack. So, your text is currently covered up by the image, which is higher up on the stack. I've fixed this and added an offset to move the text to the top right, which also wasn't working in your code.
struct ContentView: View {
#State private var agree = true
var body: some View {
ZStack(alignment: .topTrailing) {
Image(systemName: agree ? "hand.thumbsup" : "hand.thumbsdown")
.frame(width: 60, height: 60)
.foregroundColor(agree ? .green : .red)
.background(.gray)
.cornerRadius(8)
Text("15")
.foregroundColor(Color.green)
.padding(6)
.background(
Circle()
.strokeBorder(Color.green, lineWidth: 2)
.background(Circle().foregroundColor(.white))
)
.offset(x: 10, y: -10)
}
}
}

How to adjust navigationBarTitle and Label Text alignment in SwiftUI

I just learned swiftUI and I got little trouble. I want to make navigationBarTitle and title headline alignment like this:
Image 1: I want to make my view like this
I have tried to make like below but it does not work:
struct HeaderView: View {
var body: some View {
NavigationView {
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(width: 370)
Spacer()
}
.navigationBarTitle("Chelsea FC")
}
}
}
From my code above, I got a view like this:
Image 2: I got a view like this from my code above
Could someone help me how to get a view like I want
Try leading alignment
var body: some View {
NavigationView {
VStack(alignment: .leading) { // << here !!
// ... no changes in image
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.padding(.leading) // << here !!
.multilineTextAlignment(.leading)
}
You should add alignment to StackView. You can change alignment to .leading, .trailing or .center. It is centered by default thats why you are having the label in center.
var body: some View {
NavigationView {
VStack(alignment: .leading) {
// Your Code
}
}
}
Remove .frame(width: 370) and use .frame(maxWidth: .infinity) so that the text takes the whole width of its parent.
VStack {
Image("kante_training_champions_league")
.resizable()
.scaledToFill()
.frame(width: 370, height: 150)
.cornerRadius(10.0)
Text("KANTE: NEW PLAYERS DON’T SEEM NEW")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity)
Spacer()
}

Center View horizontally in SwiftUI

How can I center horizontally a View (Image) in an HStack? I want a button to be left aligned and the image to be centered horizontally the view.
Currently I have this structure:
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image("left-arrow")
.resizable()
.frame(width: 30, height: 30, alignment: .leading)
}).padding(.leading, 20)
Spacer()
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
Spacer()
}
Which is giving me this:
But I want to achieve this:
You can embed two HStack's in a ZStack and place spacers accordingly for the horizontal spacing. Embed all that in a VStack with a Spacer() to have everything pushed up to the top.
struct ContentView : View {
var buttonSize: Length = 30
var body: some View {
VStack {
ZStack {
HStack {
Button(action: {
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: CGFloat(30), height: CGFloat(30), alignment: .leading)
}).padding(.leading, CGFloat(20))
Spacer()
}
HStack {
Image(systemName: "star")
.resizable()
.frame(width: CGFloat(30), height: CGFloat(30), alignment: .center)
}
}
Spacer()
}
}
}
Note: In the second HStack, the image should automatically be center aligned, but if it isn't, you can place a Spacer() before and after the image.
Edit: Added the VStack and Spacer() to move everything to the top like the OP wanted.
Edit 2: Removed padding on image because it caused the image to be slightly offset from the center. Since it is in its own HStack and center-aligned, it does not need padding.
Edit 3: Thanks to #Chris Prince in the comments, I decided to make a simple NavigationBar-esque custom view that you can provide left, center, and right arguments to create the effect that the OP desired (where each set of views are aligned independently of each other):
struct CustomNavBar<Left, Center, Right>: View where Left: View, Center: View, Right: View {
let left: () -> Left
let center: () -> Center
let right: () -> Right
init(#ViewBuilder left: #escaping () -> Left, #ViewBuilder center: #escaping () -> Center, #ViewBuilder right: #escaping () -> Right) {
self.left = left
self.center = center
self.right = right
}
var body: some View {
ZStack {
HStack {
left()
Spacer()
}
center()
HStack {
Spacer()
right()
}
}
}
}
Usage:
struct ContentView: View {
let buttonSize: CGFloat = 30
var body: some View {
VStack {
CustomNavBar(left: {
Button(action: {
print("Tapped")
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: self.buttonSize, height: self.buttonSize, alignment: .leading)
}).padding()
}, center: {
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}, right: {
HStack {
Text("Long text here")
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
.padding(.trailing)
}.foregroundColor(.red)
})
Spacer()
Text("Normal Content")
Spacer()
}
}
}
What's about saving button size to a property and add a negative padding to the image? And pay attention to an additional spacer after the image.
struct ContentView: View {
var buttonSize: Length = 30
var body: some View {
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image(systemName: "star")
.resizable()
.frame(width: buttonSize, height: buttonSize, alignment: .leading)
})
Spacer()
Image(systemName: "star")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
.padding(.leading, -buttonSize)
Spacer()
}
Spacer()
}
}
}
The result:
Easiest way for me:
ZStack(){
HStack{
Image("star").resizable().foregroundColor(.white).frame(width: 50, height: 50)
Spacer()
}
Image("star").resizable().font(.title).foregroundColor(.white).frame(width: 50, height: 50)
}
You center the view using position property try this code
Group{ // container View
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}.position(x: UIScreen.main.bounds.width/2)
the right way to center the Title like navigationbar:
HStack {
Spacer()
.overlay {
HStack {
Image(systemName: "star")
Spacer()
}
}
Text("Title")
Spacer()
.overlay {
HStack {
Spacer()
Image(systemName: "star")
}
}
}
You can place the view that you want to center into a VStack and then set the alignment to center. Make sure that you also set the frame(maxWidth: .infinity) or else it will be centering your view in the VStack but the VStack might not take up the entire width of the screen so you might not get the appearance you are trying to achieve.
To make it even easier, write it as a function that extends the View object
extension View {
func centerInParentView() -> some View {
VStack(alignment: .center) {
self
}
.frame(maxWidth: .infinity)
}
}
And then you can just call it as you would a view modifier i.e.
VStack {
HStack {
Button(action: {
print("Tapped")
}, label: {
Image("left-arrow")
.resizable()
.frame(width: 30, height: 30, alignment: .leading)
}).padding(.leading, 20)
Spacer()
Image("twitter-logo")
.resizable()
.frame(width: 30, height: 30, alignment: .center)
}
Spacer()
}
.centerInParentView()
Works every time for me
I have got an alternative solution. I used a hidden Image as placeholder.
HStack {
Image("left-arrow").padding()
Spacer()
Image("twitter-logo")
Spacer()
// placeholder to keep layout symmetric
Image("left-arrow").padding().hidden()
}
Of course you can replace the Images with Buttons or other Views as you prefer.
Here is what worked for me
HStack {
Image(systemName: "star.fill")
.frame(maxWidth: .infinity, alignment: .leading)
Image(systemName: "star.fill")
.frame(maxWidth: .infinity, alignment: .center)
Text("")
.frame(maxWidth: .infinity, alignment: .trailing)
}
.foregroundColor(.yellow)
Inspired by SwiftUI - How to align elements in left, center, and right within HStack?
Let me propose a different solution:
https://gist.github.com/buscarini/122516641cd0ee275dd367786ff2a736
It can be used like this:
HStack {
Color.red
.frame(width: 0, height: 50)
.layoutPriority(1)
GlobalHCenteringView {
Text("Hello, world!")
.lineLimit(1)
.background(Color.green)
}
.background(Color.yellow)
Color.red
.frame(width: 180, height: 50)
.layoutPriority(1)
}
}
This will center the child view in the screen if it fits, or leave it as is if it doesn't. It is currently using UIScreen, so it only works on iOS, but you could easily pass the screen or parent width to the constructor of the view, getting it from a GeometryReader or whatever.

Resources