SwiftUI mysterious space on top of the VStack - swiftui

I have this code
NavigationView{
VStack{
GeometryReader{ geometry in
VStack{
Text("a")
}
.frame(width:geometry.size.width)
.background(Color.orange)
Spacer()
}
}
.modifier(NavBarModifier(font: self.fontUI,text: "Profile"))
}
.navigationViewStyle(StackNavigationViewStyle())
I want the
GeometryReader to appear at top of the VStack, however what I get is:
Where the blue line is the top of the VStack and orange is the GeometryReader. I tried adding Spacer() after the GeometryReader, but it didn't work. How can I remove that spacing?
struct NavBarModifier: ViewModifier{
var font: UIFont
var text: String
func body(content: Content) -> some View {
return content
.zIndex(0)
.animation(.spring())
.padding(.top,80)
.navigationBarTitle(Text(self.text),displayMode: .inline)
.navigationBarHidden(false)
.foregroundColor(.orange)
.background(NavigationConfigurator { nc in
nc.navigationBar.barTintColor = UIColor(red: 243/255, green: 107/255, blue: 21/255, alpha: 1)
nc.navigationBar.titleTextAttributes = [
.foregroundColor : UIColor.white,
.font : self.font,
.kern: 1.2
]
}
.padding([.top, .leading, .trailing]))
}
}

Just remove redundant padding in NavBarModifier
func body(content: Content) -> some View {
return content
.zIndex(0)
.animation(.spring())
// .padding(.top,80) // << this one !!

You need to add the Spacer after the GeometryReader for it to layout in the top of the VStack and also add .edgesIgnoringSafeArea(.top) for it to ignore that white space (the safe area) and make it align at the top of the VStack.
VStack {
GeometryReader{ geometry in
VStack {
Text("s")
}
.frame(width:geometry.size.width)
.background(Color.orange)
Spacer()
}
.edgesIgnoringSafeArea(.top)
Spacer()
}

Related

VStack space at top and bottom with lazyHGrid

I have this example. Why is there this space at top and bottom of the VStack? The borders of the inner views doesn't seem to be as high as the border of the VStack. I think it has something to do with the LazyHGrid!? Thanks for an answer.
struct ContentView: View{
let names = ["house","creditcard"]
var body: some View{
VStack{
Text("skldfjsjf")
Text("klsadjf")
Text("lksajf")
ScrollView(.horizontal){
LazyHGrid(
rows: [GridItem(.flexible(minimum: 40)),GridItem(.flexible(minimum: 40))],
spacing: 10) {
ForEach(names, id: \.self) { icon in
Image(systemName: icon)
.padding()
}
}
.border(Color.orange)
}
.border(Color.green)
}
.frame(width: UIScreen.main.bounds.width * 0.9)
.border(Color.black)
}
}
I'm new to SwiftUI, but I think you want the content in your VStack to be top hugging?
struct ContentView: View {
let names = ["house","creditcard"]
var body: some View{
VStack (spacing:0) { //THIS HERE FOR NO SPACE FOR VSTACK INTERNAL VIEWS
Text("skldfjsjf")
Text("klsadjf")
Text("lksajf")
ScrollView(.horizontal){
LazyHGrid(
rows: [GridItem(.flexible(minimum: 40)),GridItem(.flexible(minimum: 40))],
spacing: 10) {
ForEach(names, id: \.self) { icon in
Image(systemName: icon)
.padding()
}
}
.border(Color.orange)
}
.border(Color.green)
Spacer() //THIS HERE TO MAKE IT TOP HUGGING
}
.frame(width: UIScreen.main.bounds.width * 0.9)
.border(Color.black)
}
}

How to make List content show through a bottom bar with material background

I want the list content to show through the bottom bar like it does with the top navigation bar, creating the blur effect. I can do it with a ZStack instead of VStack and some padding on the last element of the list, but then I have to know the exact height of the bottom bar, which I'd like to avoid.
Is there a way to do it?
struct ContentView: View {
var body: some View {
NavigationView {
VStack(spacing: 0) {
List(1...30, id:\.self) { i in
Rectangle()
.foregroundColor(Color.random)
.frame(height: 36)
}
.listStyle(PlainListStyle())
Text("Bottom bar")
.font(.title)
.frame(maxWidth:.infinity)
.padding()
.background(.bar)
}
.navigationTitle("Some Nav Title")
.navigationBarTitleDisplayMode(.inline)
}
}
}
extension Color {
static var random: Color {
return Color(
red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1)
)
}
}
Try to use ZStack instead of VStack, like
ZStack(alignment: .bottom) { // << here !!
List(1...30, id:\.self) { i in
Rectangle()
.foregroundColor(Color.random)
.frame(height: 36)
}
.listStyle(PlainListStyle())
Text("Bottom bar")
.font(.title)
.frame(maxWidth:.infinity)
.padding()
.background(.regularMaterial)
}
Note: most probably you will need to add footer to the list of same height as your bar, so during scrolling to the bottom last items will be visible.

SwiftUI NavigationView and NavigationLink changes layout of custom view

I have a DetailView() that displays an image, text, and then a map of the location of the displayed image. In my ContentView() I have a NavigationView and NavigationLink to go from the main view to my custom view. Everything works fine, except that the alignment of my DetailView() is not aligned properly as when I view the preview for DetailView(). The text description is showing well below the picture. I have been pulling my hair out for 2 days trying to figure this out, but haven't so far.
Picture of ContentView()
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink(destination: DetailView(picture: "dunnottar-castle")) {
Text("Hello, World!")
Image(systemName: "sun.min.fill")
} .buttonStyle(PlainButtonStyle())
.navigationBarHidden(true)
}
}
}
=================== My DetailView()
struct MapView: UIViewRepresentable {
// 1.
func makeUIView(context: UIViewRepresentableContext<MapView>) -> MKMapView {
MKMapView(frame: .zero)
}
// 2.
func updateUIView(_ uiView: MKMapView, context: UIViewRepresentableContext<MapView>) {
// 3.
let location = CLLocationCoordinate2D(latitude: 30.478340,
longitude: -90.037687)
// 4.
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let region = MKCoordinateRegion(center: location, span: span)
uiView.setRegion(region, animated: true)
// 5.
let annotation = MKPointAnnotation()
annotation.coordinate = location
annotation.title = "Abita Springs"
annotation.subtitle = "Louisiana"
uiView.addAnnotation(annotation)
}
}
struct DetailView: View {
let picture: String
var body: some View {
VStack(spacing: -50.0){
// Picture and Title
ZStack (alignment: .bottom) {
//Image
Image(picture)
.resizable()
.aspectRatio(contentMode: .fit)
Rectangle()
.frame(height: 80)
.opacity(0.25)
.blur(radius: 10)
HStack {
VStack(alignment: .leading, spacing: 8.0) {
Text("EDINBURGH")
.foregroundColor(.yellow)
.font(.largeTitle)
}
.padding(.leading)
.padding(.bottom)
Spacer()
}
}.edgesIgnoringSafeArea(.top)
VStack{
// Description
Text("Edinburgh is Scotland's compact, hilly capital. It has a medieval Old Town and elegant Georgian New Town with gardens and neoclassical buildings. Looming over the city is Edinburgh Castle, home to Scotland’s crown jewels and the Stone of Destiny, used in the coronation of Scottish rulers. Arthur’s Seat is an imposing peak in Holyrood Park with sweeping views, and Calton Hill is topped with monuments and memorials.")
.font(.body)
.lineLimit(9)
.lineSpacing(5.0)
.padding()
// .frame(maxHeight: 310)
}
Spacer()
// Map of location
VStack {
MapView()
.edgesIgnoringSafeArea(.all)
.padding(.top)
.frame(maxHeight: 310)
// Image(systemName: "person")
.padding(.top)
}
}
}
}
I changed
NavigationLink(destination: DetailView(picture: "dunnottar-castle")) {
to
NavigationLink(destination: DetailView(picture: "dunnottar castle").edgesIgnoringSafeArea(.all)) {
and it works like I want it to now.
If you just want to have the normal layout, you can also use:
NavigationLink(destination: DetailView(picture: "dunnottar castle").navigationBarHidden(true))

List rows not centered

I am displaying some rows in a list (actually, 2 parallel lists in the example posted here), from a custom row view labeled EventRow.
In order to avoid bad UI on larger devices like an iPad, I limited the width of the EventRow to 200.
How could I get the EventRows to be centered in my lists on larger devices ?
I tried applying a .center alignment to the EventRows in the ForEach methods of my lists, tried doing the same on the lists as well, but nothing seems to work.
As an alternate solution, is there a way to set a MAX Spacer width ? I know you can set a minLength, but not a MAX it seems...
Here's a screenshot on the iPad Pro 11 sim :
The code for my EventRow file is as follows :
import SwiftUI
struct EventRow: View {
var body: some View {
VStack{
HStack {
Text("Text one")
Spacer()
Text("Text two")
}
HStack {
Spacer()
Image(systemName: "flame")
.font(.body)
Spacer()
} // END of second HStack
.padding(.top, -14)
} //END of Vstack
.frame(maxWidth: 200) // To limit width on larger devices
.listRowBackground(LinearGradient(gradient: Gradient(colors: [Color.white, Color.blue]), startPoint: .top, endPoint: .bottom))
}
}
struct EventRow_Previews: PreviewProvider {
static var previews: some View {
EventRow().previewLayout(.fixed(width: 300, height: 60))
}
}
And my ContentView :
struct ContentView: View {
struct listsSetup: ViewModifier {
func body(content: Content) -> some View {
return content
.frame(maxHeight: UIScreen.main.bounds.size.height/3)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.blue, lineWidth: 1))
.padding([.top, .bottom])
}
}
#State private var selected : Int = 0
var body: some View {
VStack {
HStack {
VStack { // 1 list Vstack
VStack {
Text("List 1")
.padding(.top)
List {
EventRow()
EventRow()
} // END of 1st List
}
.modifier(listsSetup())
} // END of 1st list VStack
VStack { // 2nd Vstack
VStack {
Text("List 2")
.padding(.top)
List {
EventRow()
EventRow()
} // END of Landings List
}
.modifier(listsSetup())
} // End of 2nd List VStack
} // End of 1st & 2nd lists HStack
.padding(.top)
Spacer()
Picker("", selection: $selected){
Text("0").tag(0)
Text("1").tag(1)
}.pickerStyle(SegmentedPickerStyle())
Text("\(selected)")
} // END of VStack
} // END of body
}
Again, any help would be appreciated.
Just embed it in one more container, like below (tested with Xcode 11.4)
VStack {
VStack{
HStack {
Text("Text one")
Spacer()
Text("Text two")
}
HStack {
Spacer()
Image(systemName: "flame")
.font(.body)
Spacer()
} // END of second HStack
.padding(.top, -14)
} //END of Vstack
.frame(maxWidth: 200) // To limit width on larger devices
}.frame(maxWidth: .infinity) // << here !!

SwiftUI list row layout

Back with a couple of SwiftUI layout questions :
I'm trying to display 2 lists side by side with custom cells.
I created the cell views (EventRow.swift) and I display them in my content view.
I added a border to my lists, for better visibility.
As you can see from the picture below, the result is ghastly:
I would like the gradient effect to be applied to the whole cell, width and height wise.
I tried setting the frame of my EventRow (using .infinity for width and height), but this crashes the app.
Since the size of EventRow is inferred, I also don't know how to adapt my row cells height to its size : you can see the horizontal delimitating bars are not fitted to my custom EventRow...
If anyone has some pointers for this, it would be greatly appreciated.
The sample project can be found here
But I also post my code below:
ContentView :
import SwiftUI
struct ContentView: View {
struct listsSetup: ViewModifier {
func body(content: Content) -> some View {
return content
.frame(maxHeight: UIScreen.main.bounds.size.height/3)
.overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.blue, lineWidth: 1))
.padding([.top, .bottom])
}
}
var body: some View {
VStack {
HStack {
VStack { // 1 list Vstack
VStack {
Text("List 1")
.padding(.top)
List {
EventRow()
EventRow()
} // END of 1st List
}
.modifier(listsSetup())
} // END of 1st list VStack
VStack { // 2nd Vstack
VStack {
Text("List 2")
.padding(.top)
List {
EventRow()
EventRow()
} // END of Landings List
}
.modifier(listsSetup())
} // End of 2nd List VStack
} // End of 1st & 2nd lists HStack
.padding(.top)
Spacer()
} // END of VStack
} // END of body
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
EventRow :
import SwiftUI
struct EventRow: View {
var body: some View {
LinearGradient(gradient: Gradient(colors: [Color.white, Color.blue]), startPoint: .top, endPoint: .bottom)
.edgesIgnoringSafeArea(.all)
.overlay(
VStack{
HStack {
Text("Text one")
Spacer()
Text("Text two")
}
HStack {
Spacer()
Image(systemName: "flame")
.font(.body)
Spacer()
} // END of second HStack
.padding(.top, -14)
} //END of Vstack
)
}
}
struct EventRow_Previews: PreviewProvider {
static var previews: some View {
EventRow().previewLayout(.fixed(width: 300, height: 60))
}
}
Edit after trying Asperi's solution :
My actual EventRow code is as follows, and the listRowBackground modifier doesn't seem to have any effect :
import SwiftUI
import CoreData
struct EventRow: View {
var event: Events
var dateFormatter: DateFormatter {
let formatter = DateFormatter()
// formatter.dateStyle = .long
formatter.dateFormat = "dd MMM yy"
return formatter
}
var body: some View {
VStack{
HStack {
Text(event.airportName ?? "")
.font(.headline)
Spacer()
Text(self.dateFormatter.string(from: event.eventDate!))
.font(.body)
}
HStack {
Text(event.flightNumber ?? "")
.font(.body)
Spacer()
if event.isSimulator {
Image(systemName: "s.circle")
.font(.body)
} else {
Image(systemName: "airplane")
.font(.body
)
}
Spacer()
if event.aircraftType == 0 {
Text("")
.font(.body)
} else if event.aircraftType == 1 {
Text("330")
.font(.body)
} else if event.aircraftType == 2 {
Text("350")
.font(.body)
}
} // END of second HStack
.padding(.top, -14)
} //END of Vstack
.listRowBackground(LinearGradient(gradient: Gradient(colors: [Color.white, Color.blue]), startPoint: .top, endPoint: .bottom))
}
}
struct EventRow_Previews: PreviewProvider {
static var previews: some View {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newEvent = Events(context: context)
newEvent.eventDate = Date()
newEvent.aircraftType = 1
newEvent.airportName = "LDG tst"
newEvent.flightNumber = "AF TEST"
newEvent.id = UUID()
newEvent.isLanding = true
newEvent.isSimulator = false
return EventRow(event: newEvent).environment(\.managedObjectContext, context)
.previewLayout(.fixed(width: 300, height: 60))
}
}
Here is a solution to make gradient row-wide
struct EventRow: View {
var body: some View {
VStack{
HStack {
Text("Text one")
Spacer()
Text("Text two")
}
HStack {
Spacer()
Image(systemName: "flame")
.font(.body)
Spacer()
} // END of second HStack
.padding(.top, -14)
} //END of Vstack
.listRowBackground(LinearGradient(gradient: Gradient(colors: [Color.white, Color.blue]), startPoint: .top, endPoint: .bottom)
)
}
}