49

I'm trying to create a view in SwiftUI. In the preview, it looks like it should, but when run on my iPhone (or on the live preview) it looks like it is offset.

I tried to set the padding to -150, but then the TextField doesn't respond to touches.

VStack {
    Text("Name:")
        .padding(.bottom, 1)
    TextField($name)
        .padding(.horizontal, 25.0)
        .textFieldStyle(.roundedBorder)
        .frame(maxWidth: 500)
    Text("Image:")
        .padding(.top, 1)
    Image(uiImage: image!)
        .resizable(capInsets: EdgeInsets(), resizingMode: .stretch)
        .scaledToFit()
        .frame(width: 250, height: 250)
        .clipShape(RoundedRectangle(cornerRadius: 10))
        .padding(.top, 5)
    Button(action: {
        withAnimation {
            self.showImagePicker = true
        }
    }) {
        Text("Select Image")
            .color(.init(red: 20/255, green: 146/255, blue: 81/255))
    }
    Button(action: {
        let list = LSList( title: self.name,
                                           image: self.image!,
                                           id: 0)
        list.add()
        self.userData.listsData.append(list)
    }) {
        Text("Add List")
            .color(.white)
            .font(.system(size: 25))
            .bold()
            .padding(.horizontal, 7)
            .frame(height: 35)
            .background(Color.green)
            .clipShape(RoundedRectangle(cornerRadius: 3))
    }
    Spacer()
} .navigationBarTitle(Text("Add List"))

The view in the preview: The view in the preview

The view on my iPhone: The view on my iPhone

Shahar Melamed
  • 1,534
  • 5
  • 15
  • 23

5 Answers5

90

I had the same issue, but found the the problem I had was in using navigationView multiple times.

I thought that we should have NavigationView in every view, but apparently, we should place navigationView only in the main view of the application, and all other views that we enter via navigationLink, automatically get the back option without the need to mention NavigationView again.

So check if you use navigationView more than once in your code.

Strangely, we can specify navigationBarTitle even in views that dont have the navigationView mentioned in them, this is because the navigationView is at the parent view. Use "displayMode: .inline", to make the navigation area as minimal as possible, it will save you real-estate.

Another thing to do, is to use Spacer(). If you want all your items to be at the top, just put Spacer() as the last item in VStack, and it will push all items to the top.

See this example:

VStack {
    // What you care about displaying
    Text("Something to Show 1")
    Text("Something to Show 2")
    Text("Something to Show 3")

    // This should be the last, put everything to the top
    Spacer()             
}
.navigationBarTitle(Text("The Title"), displayMode: .inline)
Guy Nir
  • 1,098
  • 9
  • 7
  • Hi iSpain, I have navigationLink in my code and it is working, can you please share your code ? – Guy Nir Jan 26 '20 at 17:34
  • oh my, thank you. I was having the same issue with a child view being a navigation view. I have not found any documentation that says you should only use the navigation view at the top of the stack, but it sure looks like that is required. – neils4fun Mar 04 '20 at 23:25
  • 1
    This should be the accepted answer for SwiftUI adding VStack(aligment: .leading) { Spacer() } and adding the Spacer() to be the last element in the VStack. – Juan Ricardo Nov 20 '20 at 00:38
  • Thank you @Juan Ricardo. I didn't mention the (alignment: .leading) because it really depends on what the developers wants to align to, alignment to .center can be a valid solution as well, depends on the needs. – Guy Nir Nov 28 '20 at 06:11
44

I had a similar issue, I wanted a VStack to align its content on the top of the screen, and was only using a NavigationView in the parent, but the VStack showed centered. What fixed it for me was using a Spacer(), like this:

    VStack(alignment: .leading, spacing: 10){
        HStack(alignment: .center, spacing: 5.0) {
            ...
        }
        Group{
            ...
        }
        Spacer() //<-- This solved my problem
   }
GAlonso
  • 516
  • 5
  • 7
8

Assuming that your VStack is wrapped in a NavigationView, this is the reason it is not rendering correctly in the simulator. The reason it shows fine in preview is that it is not displaying the navigation bar (which includes the back button as well) because the canvas doesn’t know this view might be pushed but in runtime the navigation bar is added while you’re also using an extra NavigationView as well.

To fix it unwrap the VStack from NavigationView and simply remove this NavigationView from the child view.

M Reza
  • 18,350
  • 14
  • 66
  • 71
  • 1
    I tried to remove it from the VStack, but nothing changed. I changed the background of the stack to blue to see if it’s height is ok and that’s what happened: https://imgur.com/a/76vQIkG – Shahar Melamed Jun 11 '19 at 05:11
  • @Shahar The Nav bar problem is fixed in your screenshot! It's fixed to the top. The problem is with probably with VStack now. I will update my post. – M Reza Jun 11 '19 at 13:33
  • @Shahar remove any extra padding/offset you've added. Just try the code you've provided in your question. It's working fine for me! – M Reza Jun 11 '19 at 13:48
  • If you don't want to remove the `NavigationView`, you can set `.navigationBarHidden(true)` on the `VStack`. – nonamorando Mar 18 '21 at 20:05
0
  1. To make it visible correct with Navigation items in previews(even for child views) you need to wrap the View into the NavigationView { }, because the Canvas is an empty clean environment and it doesn't know that the View may be pushed into the NavigationView
  2. To fix it on iPhone makes sure you are correctly pushing the child view. It should be NavigationButton(destination: AddList()) {}

Example of RowView implementation:

   NavigationButton(destination: CarDetail(car: car)) {
      HStack {
        VStack(alignment: .leading) {
          Text(car.name)
          Text(car.isAvailable ? "Is Available" : "Out of stock")
            .font(.subheadline)
            .foregroundColor(.secondary)
        }
      }
    }
DenFav
  • 2,683
  • 1
  • 18
  • 27
  • It's inside a `NavigationView { }`, the NavigationButton inside the list: `NavigationButton(destination: AddList().environmentObject(self.userData)) { Text("Add List") }` – Shahar Melamed Jun 10 '19 at 19:27
-3
 ScrollView(.vertical){
   VStack {
    Text("Name:")
        .padding(.bottom, 1)
    TextField($name)
        .padding(.horizontal, 25.0)
        .textFieldStyle(.roundedBorder)
        .frame(maxWidth: 500)
    Text("Image:")
        .padding(.top, 1)
    Image(uiImage: image!)
        .resizable(capInsets: EdgeInsets(), resizingMode: .stretch)
        .scaledToFit()
        .frame(width: 250, height: 250)
        .clipShape(RoundedRectangle(cornerRadius: 10))
        .padding(.top, 5)
    Button(action: {
        withAnimation {
            self.showImagePicker = true
        }
    }) {
        Text("Select Image")
            .color(.init(red: 20/255, green: 146/255, blue: 81/255))
    }
    Button(action: {
        let list = LSList( title: self.name,
                                           image: self.image!,
                                           id: 0)
        list.add()
        self.userData.listsData.append(list)
    }) {
        Text("Add List")
            .color(.white)
            .font(.system(size: 25))
            .bold()
            .padding(.horizontal, 7)
            .frame(height: 35)
            .background(Color.green)
            .clipShape(RoundedRectangle(cornerRadius: 3))
    }
    Spacer()
} .navigationBarTitle(Text("Add List"))
}