I am currently creating a content view for my application and am experiencing some strange behavior with padding. As seen in the photo below, there is quite a bit of space below the navigation bar at the top of the phone. I don't specify any padding here so I'm wondering why there is so much space between the top and where the image is displayed. The image doesn't have that large of a white box around it either.
My code does not specify any kind of margin or padding. I'm new to Swift and SwiftUI so I'm curious if there is some automatic padding applied to navigation views?
import Kingfisher
struct BottleView: View {
let bottle: Bottle
var body: some View {
VStack {
KFImage(URL(string: bottle.image ?? "")!)
.resizable()
.frame(width: 128, height: 256)
VStack(alignment: .leading) {
HStack {
Text(bottle.name)
.font(.title)
Spacer()
Text("Price")
}
HStack {
Text(bottle.varietal ?? "")
Spacer()
Text("$\(bottle.price ?? "")")
}
.font(.subheadline)
.foregroundColor(.secondary)
VStack(alignment: .leading) {
Text("Information")
.font(.title2)
Text(bottle.information ?? "")
}
}
}
}
}
If you apply a .background(Color.red) to the VStack, you'll see that it's centered in the screen.
var body: some View {
VStack {
Image("TestImage")
.resizable()
.frame(width: 128, height: 256)
/// ... more code
}
.background(Color.red)
}
This is because, by default, most SwiftUI views are centered. For example, try just a Text:
struct ContentView: View {
var body: some View {
Text("Hello, I'm centered!")
}
}
So if you don't want it centered, this is where Spacers come in. These expand to fill all available space, pushing all other views. Here's the code that gets rid of the "bit of space below the navigation bar at the top of the phone":
(note that I replaced your Bottle properties with static text, make sure you change them back)
struct BottleView: View {
// let bottle: Bottle
var body: some View {
VStack {
Image("TestImage")
.resizable()
.frame(width: 128, height: 256)
VStack(alignment: .leading) {
HStack {
Text("Blantons")
.font(.title)
Spacer()
Text("Price")
}
HStack {
Text("Bourbon")
Spacer()
Text("$59.99")
}
.font(.subheadline)
.foregroundColor(.secondary)
VStack(alignment: .leading) {
Text("Information")
.font(.title2)
Text("Product info here")
}
}
Spacer() /// spacer right here! pushes the other views up
}
.background(Color.red)
.navigationBarTitleDisplayMode(.inline) /// get rid of the additional top gap that the default Large Title navigation bar produces
}
}
Result:
Related
I'm looking how to align text to the top of screen, do you know how to?
import SwiftUI
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.padding(.bottom, 5.0)
Text("Please sign in to your account")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(Color.gray)
.multilineTextAlignment(.center)
}
}
}
struct RegisterSignInScreen_Previews: PreviewProvider {
static var previews: some View {
RegisterSignInScreen()
}
}
image
I tried to embed my VStack in a ZStack:
ZStack(alignment: .top)
But it didn't work.
To see why this is happening you can add a background to your VStack or ZStack
ZStack {
VStack {
//Text items
}
}
.background(.red)
What you will notice (maybe unexpectedly) is that the stack is only as big as it needs to be to contain the text items.
When you add alignment to the stack it aligns the content within the stack to the specified edge of the relatively small stack. It doesn't align the stack to an edge of the screen, or change the size of the stack. What you're actually looking to do is align the stack, not it's content.
In order to align to the top of the screen you can make the stack fill the entire height of the screen. Either by adding a Spacer() at the bottom of the VStack that will fill the remaining vertical space pushing the content upwards, or by applying a frame with .infinity maxHeight: and top aligned content to the VStack.
VStack {
Text("Hello World!")
Spacer()
}
VStack {
Text("Hello World!")
}
.frame(maxHeight: .infinity, alignment: .top)
Alternatively if required for your view, you can do a similar thing with a second stack containing your text stack like so:
var body: some View {
VStack {
welcomeText
Spacer()
}
}
var welcomeText: some View {
VStack {
Text("Welcome Back!")
.font(.title)
.fontWeight(.bold)
.multilineTextAlignment(.center)
.padding(.bottom, 5.0)
Text("Please sign in to your account")
.font(.subheadline)
.fontWeight(.bold)
.foregroundColor(Color.gray)
.multilineTextAlignment(.center)
}
}
You can do this in a couple of ways, but the most obvious way is to simply add a Spacer() in the bottom of the VStack
Like so:
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
Text("Please sign in to your account")
Spacer() // <- HERE
}
}
}
This will push your content in the VStack to the top.
Alternatively, you can add a frame modifier and force your VStack height and add the alignment there using .frame
struct RegisterSignInScreen: View {
var body: some View {
VStack {
Text("Welcome Back!")
Text("Please sign in to your account")
}
.frame(maxHeight: .infinity, alignment: .top) // <- HERE
}
}
ZStack(alignment: .top) works, but not the same way as you think)
try to do:
ZStack(alignment: .top){
}
.background(Color.green)
and you will understand why so :)
you need to use spacer :)
VStack {
YourView()
Spacer()
}
I'm posting this question again, with a simplified view of my code and views. I'll delete the old one since I think I didn't explain my issue properly there (and it has no answers yet).
I'm trying to position a HStack with an initial height at the bottom of the screen. With the top portion being a scroll view. The HStack might expand to a certain extent as more text is typed into the Text Editor.
I am able to achieve this, as long as there is no Text Editor.
I need help with figuring out how to do this with Text Editor.
Please see below -
With just a text view
With a text editor instead of text view
Here's the code for it -
struct ContentView: View {
#State var myText: String = "This Text Editor is screaming \n\n THIS IS SPARTA!!! \n\n at me and kicking me into the abyss of similarly worded Stackoverflow questions."
var body: some View{
VStack(spacing:0){
GeometryReader {geo in
ScrollView(.vertical, showsIndicators: false){
ForEach(1 ..< 200, id: \.self){ num in
Text("\(num)")
.frame(width: geo.size.width)
.padding(5)
.background(.yellow)
}
}
.background(.orange)
.frame(minWidth: geo.size.width, maxHeight: geo.size.height * 0.96 , alignment: .top)
}
HStack(spacing: 0){
Text("This HStack is supposed to contain a text editor that expands as needed to a max height but starts off at this height.")
.padding()
.background(.teal)
/*TextEditor(text: $myText)
.multilineTextAlignment(.center)
.font(.title3)
.padding()*/
}
.frame(minHeight:50)
.background(.teal)
}
}}
Any help in the right direction is greatly appreciated!
See the following with comments:
struct ContentView: View {
#State var myText: String = "This Text Editor is screaming \n\n THIS IS SPARTA!!! \n\n at me and kicking me into the abyss of similarly worded Stackoverflow questions."
var body: some View{
VStack(spacing: 0) {
ScrollView(.vertical, showsIndicators: false) {
ForEach(1 ..< 200, id: \.self) { num in
Text("\(num)")
// The GeometryReader is unnecessary. Use a frame with .infinity to expand your row.
.frame(maxWidth: .infinity)
.background(.yellow)
.padding(5)
}
}
.background(.orange)
HStack(spacing: 0) {
TextEditor(text: $myText)
.multilineTextAlignment(.center)
.font(.title3)
// Use .fixedSize to reduce the height of the TextEditor to its minimal height.
.fixedSize(horizontal: false, vertical: true)
// this .padding() gives you space around your TextEditor for margins
.padding()
// this background gives you white margins
.background(Color.white)
// this .padding() keeps the background above from resizing to the size of the teal background.
.padding()
}
.frame(minHeight: 50)
.background(.teal)
}
}
}
I have a view that consists of multiple UI elements in a HStack:
Time
Icon
Summary
Divider
Value
I want 1), 2) and 5) to take up no more space than necessary i.e. I don't need to intervene.
With 3) and 4) I would like for the summary to take up as much space as it needs at the expense of the divider that follows. If that means none, or a minimal amount, of the divider can appear then so be it. However, despite my best attempts, the summary only seems to take up a certain amount of space, causing its content to run over multiple lines to ensure the divider always appears. Giving it a layoutPriority helped slightly.
I don't want to give it a minWidth because I'd like for the distribution of width to be dynamically arranged based on the content.
Here's the code:
HStack {
// MARK: Time
HStack {
VStack {
Spacer()
Text("9am")
Spacer()
}
}.padding(.horizontal, 4)
.padding(.vertical)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
VStack {
HStack {
// MARK: Icon
Image(systemName: "cloud.drizzle.fill")
.font(Font.title2)
// MARK: Summary
VStack {
HStack {
Text("Humid and overcast")
.padding(.trailing, 2)
.background(Color.white)
.layoutPriority(100)
Spacer()
}
}
// MARK: Line
VStack {
Spacer()
Divider()
Spacer()
}
// MARK: Values
VStack {
Text(String(22.66))
Text("25%")
.foregroundColor(Color.black)
.background(Color.white)
}
Spacer()
}
}
}
And this is what it looks like:
As you can the text runs over multiple lines when there's enough room for it to fit on one line, as long as the divider that followed accommodated the longer text by being shorter.
Fixed size should be helpful here. Tested with Xcode 12.1 / iOS 14.1
struct TestLongHStack: View {
var body: some View {
HStack {
// MARK: Time
HStack {
VStack {
Spacer()
Text("9am")
Spacer()
}
}.padding(.horizontal, 4)
.padding(.vertical)
.background(Color.blue.opacity(0.2))
.cornerRadius(8)
VStack {
HStack {
// MARK: Icon
Image(systemName: "cloud.drizzle.fill")
.font(Font.title2)
// MARK: Summary
VStack {
HStack {
Text("Humid and overcast").fixedSize() // << here !!
.padding(.trailing, 2)
.background(Color.white)
.layoutPriority(100)
Spacer()
}
}
// MARK: Line
VStack {
Spacer()
Divider()
Spacer()
}
// MARK: Values
VStack {
Text(String(22.66)).bold().italic()
Text("25%").font(.footnote)
.foregroundColor(Color.black)
.background(Color.white)
}
Spacer()
}
}
}.fixedSize(horizontal: false, vertical: true)
}
}
There is a lot of VStack / HStack / Divider / Spacer going on that can be optimized using a few HStack properties. Below is the exact same display with much less layout code and getting rid of the whacky Divider element:
struct TesterView: View {
var body: some View {
HStack(alignment: .center, spacing: 10) { // use alignment and spacing instead of all the VStack formatting
// MARK: Time
Text("9am")
.padding(.vertical, 20).padding(.horizontal, 5) // specifically set a padding number instead of relying on default
.background(Color.blue.opacity(0.2)
.cornerRadius(8))
// MARK: Icon
Image(systemName: "cloud.drizzle.fill")
.font(.title2)
// MARK: Summary
Text("Humid and overcast")
.fixedSize(horizontal:true, vertical:false)
.background(Color.white)
// MARK: Line
// note: Rectangle expands to fill space just like Divider, use .frame() to control its height
// IMO relying on Divider to interpret its parent container and expand properly seems whacky and non-specific
Rectangle()
.fill(Color.black.opacity(0.2))
.padding(.horizontal, 5)
.frame(height:1)
// MARK: Values
VStack {
Text(String(22.66)).bold().italic()
Text("25%").font(.footnote)
.foregroundColor(Color.black)
.background(Color.white)
}
}
.padding(.horizontal, 20) // added HStack padding to push off screen edges
}
}
I'm using SwiftUI to create a Widget and I'm struggling with something that is pretty simple and straightforward in Swift.
I have 2 images that are next to each other and I want them with the exact same size with aspect ratio fill but without getting out of bounds.
How it works at the moment is I have a view that is an Image and a Text. And then a parent view that has a HStack with 2 of those views.
Basically what I want to achieve is this view but with the images correctly:
This is done doing this:
VStack() {
Image(uiImage: image)
.resizable()
Text(affirmation.title)
.font(.body)
.foregroundColor(Color(UIColor.MAPurple()))
}
}
And for the parent view:
HStack {
Spacer()
CardView(text: text, image: firstImage)
Spacer()
CardView(text: text, image: secondImage)
Spacer()
}
If I add the aspect ratio to fill as I would do in Swift, this is how it looks:
Update
Adding a minimal reproducible example:
struct CardView: View {
let text: String
let image: UIImage
var body: some View {
VStack(alignment: .leading) {
Image(uiImage: image)
.resizable()
// .aspectRatio(contentMode: .fill)
.clipped()
Text(text)
.font(.body)
.multilineTextAlignment(.leading)
.foregroundColor(Color.blue)
}
}
}
struct ParentView: View {
let firstText: String = "This is something"
let firstImage: UIImage
let secondText: String = "This is something else"
let secondImage: UIImage
let top: String = "This is the top string"
var body: some View {
VStack {
Spacer()
Spacer()
Text(top)
.font(.largeTitle)
.foregroundColor(Color(UIColor.MAPurple()))
.padding(.all, 10)
.minimumScaleFactor(0.4)
Spacer()
Spacer()
HStack {
Spacer()
CardView(text: firstText, image: firstImage)
Spacer()
CardView(text: secondText, image: secondImage)
Spacer()
}
Spacer()
Spacer()
}
.background(Color.yellow)
.edgesIgnoringSafeArea(.all)
}
}
Giving proper height to the image solves the problem.
Seperated Horizontal images view and text views seperately. As the height of the text may increase according to the content, but image should align properly. Given maximum width to the Text as it should not go beyond Image width.
CardTextView aligns both texts in HStack below images
struct CardTextView: View {
let text: String
var body: some View {
return Text(text)
.font(.body)
.multilineTextAlignment(.leading)
.lineLimit(nil)
.foregroundColor(Color.blue)
.frame(minWidth: 0, maxWidth:170)
}
}
CardImageView aligns both images in HStack below main title
struct CardImageView: View {
let image: UIImage
var body: some View {
return Image(uiImage: image)
.resizable()
.frame(width:170, height: 170)
.cornerRadius(12)
}
}
Finally ParentView : added spacing for both VStack and HStack
struct ParentView: View {
let firstText: String = "This is something"
let firstImage: UIImage
let secondText: String = "This is something else a looonnnnnggggg texttttttttttt"
let secondImage: UIImage
let top: String = "This is the top string"
var body: some View {
VStack(spacing: 12) {
Text(top)
.font(.title)
.foregroundColor(Color(UIColor.purple))
.padding(.top, 20)
HStack(spacing: 12) {
CardImageView(image: firstImage)
CardImageView(image: secondImage)
}
HStack(spacing: 12) {
CardTextView(text: firstText)
CardTextView(text: secondText)
}.padding(.bottom, 20)
}.padding(16)
.background(Color.yellow).cornerRadius(14)
}
}
I guess this is what you are expecting.
I am trying to change background color main this view but unable to do it. I tried to put background(Color.green) at HStack, VSTack and even on ZStack but it did not work, not sure if i am putting at right place. By default it is taking phone or simulator color which is white but i want to apply custom background color
My Xcode version is 11.5
struct HomePageView: View {
#State var size = UIScreen.main.bounds.width / 1.6
var body: some View {
GeometryReader{_ in
VStack {
HStack {
ZStack{
// main home page components here....
NavigationView{
VStack {
AssignmentDaysView()
}.background(Color.lairBackgroundGray)
.frame(width: 100, height: 100, alignment: .top)
.navigationBarItems(leading: Button(action: {
self.size = 10
}, label: {
Image("menu")
.resizable()
.frame(width: 30, height: 30)
}).foregroundColor(.appHeadingColor), trailing:
Button(action: {
print("profile is pressed")
}) {
HStack {
NavigationLink(destination: ProfileView()) {
LinearGradient.lairHorizontalDark
.frame(width: 30, height: 30)
.mask(
Image(systemName: "person.crop.circle")
.resizable()
.scaledToFit()
)
}
}
}
).navigationBarTitle("Home", displayMode: .inline)
}
HStack{
menu(size: self.$size)
.cornerRadius(20)
.padding(.leading, -self.size)
.offset(x: -self.size)
Spacer()
}
Spacer()
}.animation(.spring()).background(Color.lairBackgroundGray)
Spacer()
}
}
}
}
}
struct HomePageView_Previews: PreviewProvider {
static var previews: some View {
HomePageView()
}
}
In your NavigationView you have a VStack. Instead you can use a ZStack and add a background below your VStack.
Try the following:
NavigationView {
ZStack {
Color.green // <- or any other Color/Gradient/View you want
.edgesIgnoringSafeArea(.all) // <- optionally, if you want to cover the whole screen
VStack {
Text("assignments")
}
.background(Color.gray)
.frame(width: 100, height: 100, alignment: .top)
}
}
Note: you use many stacks wrapped in a GeometryReader which you don't use. Consider simplifying your View by removing unnecessary stacks. Also you may not need a GeometryReader if you use UIScreen.main.bounds (however, GeometryReader is preferred in SwiftUI).
Try removing some layers: you can start with removing the top ones: GeometryReader, VStack, HStack...
Try the following:
Change the view background color especially safe area also
struct SignUpView: View {
var body: some View {
ZStack {
Color.blue //background color
}.edgesIgnoringSafeArea(.all)