In the code below there is no extra padding added. However in the preview or simulator, there is extra padding/spacing in TextField and also for map view. Is there any modifier to remove the extra spacing
import SwiftUI
import CoreLocation
import MapKit
struct LocationView: View {
#State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
#State private var addressSearchString:String = ""
var body: some View {
VStack {
Text("Confirm Your Location").foregroundColor(.black).background(.blue)
TextField("Search for area, street name...", text: $addressSearchString)
.background(.blue.opacity(0.3))
ZStack(alignment:.bottom) {
Map(coordinateRegion: $region, interactionModes: [.all])
Button(action: {}) {
HStack {
Text("Locate Me")
}
}
.foregroundColor(.green)
.background(.white)
}
Text("Test 1")
.background(.blue)
Text("Test 2")
.background(.green)
Text("Test 3")
.background(.blue)
Text("Test 4")
.background(.green)
Text("Test 5")
.background(.blue)
}.background(.gray)
}
}
Remove the distance between adjacent subviews.
VStack(spacing: 0) { /* NEW */
Text("Confirm Your Location").foregroundColor(.black).background(.blue)
TextField("Search for area, street name...", text: $addressSearchString)
.background(.blue.opacity(0.3))
ZStack(alignment:.bottom) {
Map(coordinateRegion: $region, interactionModes: [.all])
Button(action: {}) {
HStack {
Text("Locate Me")
}
}
.foregroundColor(.green)
.background(.white)
}
Text("Test 1")
.background(.blue)
Text("Test 2")
.background(.green)
Text("Test 3")
.background(.blue)
Text("Test 4")
.background(.green)
Text("Test 5")
.background(.blue)
}.background(.gray)
Related
I'm new to SwiftUI, and I have a simple app with a ZStack:
struct ContentView: View {
#State var num : Int = 1
var body: some View {
NavigationView{
ZStack{
Text("asd")
.foregroundColor(.blue)
.frame(width: 400, height: 400, alignment: .center)
.background(.blue)
VStack{
List{
ListItem()
ListItem()
}
}
.toolbar{
Button{
num+=1
} label: {
Label("Add", systemImage: "plus")
}
}
}
}
}
}
The problem is that the blue frame with the text is not displayed:
Why is this happening?
You are using ZStack to wrap up everything.
Solution: Change from ZStack to VStack.
NavigationView{
VStack{ //this part
It is because the Text View is underneath the List in the ZStack.
If you move the Text to after the list, it will be shown on top.
ZStack {
VStack{
List {
ListItem()
ListItem()
}
.listStyle(.insetGrouped)
}
.toolbar{
Button{
num+=1
} label: {
Label("Add", systemImage: "plus")
}
}
Text("asd")
.foregroundColor(.blue)
.frame(width: 400, height: 400, alignment: .center)
.background(.blue)
}
I have some vstack views, and a zstack view (its a dropdown menu).
I need the top of the zstack view to align itself perfectly with the text of VStack 2 Text like this
I think I need to utilize named coordinate spaces, but I have been unsuccessful in accomplishing this
You can use a custom alignmentGuide:
struct ContentView: View {
#State private var selection: Int = 1
var body: some View {
ZStack(alignment: .myAlignment) { // important
VStack(spacing: 0) {
Text("VStack 1 - press long")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(.blue)
Text("VStack 2")
.padding(.trailing)
// defining alignment point on "VStack 2" Text
.alignmentGuide(.myVerticalAlignment, computeValue: { d in
d[VerticalAlignment.top]
})
.alignmentGuide(.myHorizontalAlignment, computeValue: { d in
d[HorizontalAlignment.trailing]
})
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(.orange)
Text("VStack 3")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(.pink)
Text("VStack 4")
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(.green)
}
// Overlay
VStack {
Text("Item 1")
Text("Item 2")
Text("Item 3")
Text("Item 4")
Text("Item 5")
}
.padding()
.background(.gray)
// alignment of overlay
.alignmentGuide(.myVerticalAlignment, computeValue: { d in
d[VerticalAlignment.top]
})
.alignmentGuide(.myHorizontalAlignment, computeValue: { d in
d[HorizontalAlignment.leading]
})
}
}
}
extension VerticalAlignment {
private enum MyVerticalAlignment : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.bottom]
}
}
static let myVerticalAlignment = VerticalAlignment(MyVerticalAlignment.self)
}
extension HorizontalAlignment {
private enum MyHorizontalAlignment : AlignmentID {
static func defaultValue(in d: ViewDimensions) -> CGFloat {
return d[.leading]
}
}
static let myHorizontalAlignment = HorizontalAlignment(MyHorizontalAlignment.self)
}
extension Alignment {
static let myAlignment = Alignment(horizontal: .myHorizontalAlignment, vertical: .myVerticalAlignment)
}
But a much easier way for a dropdown menu would be using either .contextMenu (appearing on long press)
Text("VStack 1 - press long")
// option 1
.contextMenu {
Button("Item 1") {}
Button("Item 2") {}
Button("Item 3") {}
}
or a Menu style picker:
HStack {
Text("VStack 3")
// option 2
Picker("Menu", selection: $selection) {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
.pickerStyle(.menu)
}
I have following problem. I want to create a vertical ScrollView with many rows. At the bottom of the view I have an info bar which appears over the scroll view because I put all the items in a ZStack. Here is my code and what it produces:
struct ProblemView: View {
var body: some View {
ZStack {
ScrollView(.vertical, showsIndicators: true) {
VStack {
ForEach(0..<100, id:\.self) {i in
HStack {
Text("Text \(i)")
.foregroundColor(.red)
Spacer()
Image(systemName: "plus")
.foregroundColor(.blue)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
Divider()
}
}
}
VStack {
Spacer()
HStack {
Text("Some Info here")
Image(systemName: "info.circle")
.foregroundColor(.blue)
}
.padding()
.frame(maxWidth: .infinity)
.ignoresSafeArea()
.background(.ultraThinMaterial)
}
}
}
}
struct ProblemView_Previews: PreviewProvider {
static var previews: some View {
ProblemView()
}
}
As you can see the drag indicator is hidden behind the info frame. Also the last item can't be seen because it is also behind the other frame. What
I want is that the drag indicator stops at this info frame. Why am I using a ZStack and not just a VStack? I want that this opacity effect behind the info frame, you get when you scroll.
A edit on my preview post has been added and therefore I cannot edit it... I am just gonna post the answer as an other one then.
This is the code that fixes your problem:
import SwiftUI
struct ProblemView: View {
var body: some View {
ScrollView {
VStack {
ForEach(0..<100, id:\.self) {i in
HStack {
Text("Text \(i)")
.foregroundColor(.red)
Spacer()
Image(systemName: "plus")
.foregroundColor(.blue)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
Divider()
}
}
.frame(maxWidth: .infinity)
}
.safeAreaInset(edge: .bottom) { // 👈🏻
VStack {
HStack {
Text("Some Info here")
Image(systemName: "info.circle")
.foregroundColor(.blue)
}
.padding()
.frame(maxWidth: .infinity)
.ignoresSafeArea()
.background(.ultraThinMaterial)
}
}
}
}
struct ProblemView_Previews: PreviewProvider {
static var previews: some View {
ProblemView()
}
}
We cannot control offset of indicator, but we can make all needed views visible by injecting last empty view with the same height (calculated dynamically) as info panel.
Here is possible approach. Tested with Xcode 13.2 / iOS 15.2
struct ProblemView: View {
#State private var viewHeight = CGFloat.zero
var body: some View {
ZStack {
ScrollView(.vertical, showsIndicators: true) {
VStack {
ForEach(0..<100, id:\.self) {i in
HStack {
Text("Text \(i)")
.foregroundColor(.red)
Spacer()
Image(systemName: "plus")
.foregroundColor(.blue)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
Divider()
}
Color.clear
.frame(minHeight: viewHeight) // << here !!
}
}
VStack {
Spacer()
HStack {
Text("Some Info here")
Image(systemName: "info.circle")
.foregroundColor(.blue)
}
.padding()
.frame(maxWidth: .infinity)
.ignoresSafeArea()
.background(.ultraThinMaterial)
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height)
})
}
}
.onPreferenceChange(ViewHeightKey.self) {
self.viewHeight = $0
}
}
}
struct ViewHeightKey: PreferenceKey {
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = value + nextValue()
}
}
I embedded a TabView in a NavigationView and the Text in the view gets pushed down slightly. I've tried moving the views around however it just ends up breaking functionally. You can see the green text is not vertically aligned to the rest of the device, but instead aligned to the content under the navigation bar.
var body: some View {
NavigationView{
TabView {
Text("TEST 1").foregroundColor(color).font(Font.custom("Catamaran-ExtraBold", size: 48)).navigationTitle("TEST 1")
Text("TEST 2").foregroundColor(color).font(Font.custom("Catamaran-ExtraBold", size: 48)).navigationTitle("TEST 2")
}
.foregroundColor(.black).navigationBarTitleDisplayMode(.inline)
.font(Font.custom("Catamaran-ExtraBold", size: 20))
.navigationBarItems(
leading:
NavigationLink(destination: SettingsView(), label: {
Image(systemName: "gearshape")
})).foregroundColor(colorScheme == .dark ? .white : .black)
}
.navigationViewStyle(.stack)
.ignoresSafeArea()
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
It's a bit strange what you are trying to achieve. Centering it as if the NavigationView wasn't there means that the view is no longer centered in its container. It will look strange and not centered like this.
However, here is how you can achieve this. This example uses GeometryReaders to measure the height of the navigation bar, and take away half that so the text now appears centered to the safe area.
Before:
struct ContentView: View {
var body: some View {
NavigationView {
TabView {
Text("TEST 1")
.font(.title.bold())
.foregroundColor(.green)
.navigationTitle("TEST 1")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red.opacity(0.1))
Text("TEST 2")
.font(.title.bold())
.foregroundColor(.green)
.navigationTitle("TEST 2")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red.opacity(0.1))
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
.navigationViewStyle(.stack)
}
}
After:
struct ContentView: View {
var body: some View {
GeometryReader { geoRoot in // <- HERE
NavigationView {
GeometryReader { geo in // <- HERE
let yOffset = (geoRoot.safeAreaInsets.top - geo.safeAreaInsets.top) / 2 // <- HERE
TabView {
Text("TEST 1")
.font(.title.bold())
.foregroundColor(.green)
.navigationTitle("TEST 1")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red.opacity(0.1))
// <- Also replicate offset here, not done for demo
Text("TEST 2")
.font(.title.bold())
.foregroundColor(.green)
.navigationTitle("TEST 2")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.red.opacity(0.1))
.offset(y: yOffset) // <- HERE
}
.tabViewStyle(.page)
.indexViewStyle(.page(backgroundDisplayMode: .always))
}
}
.navigationViewStyle(.stack)
}
}
}
Result
Before: TEST 1 tab
After: TEST 2 tab
I have an UI with few buttons. I want to navigate to detail view on button click. For the first time it navigates successfully but when comeback to root view and again I click on the button, I doesn't navigates.
I have followed all the tutorials and I can see the same code which I have used but I am not getting what is the issue occurring.
State of the variable
#State var google: Int? = 0 has been defied in view class struct ContentView: View
Complete code of the root View
struct ContentView: View {
#State var username: String = ""
#State var password: String = ""
#State var selection: Int? = nil
#State var google: Int? = 0
#State var buttton1: Int? = nil
var body: some View {
NavigationView{
VStack{
Spacer()
VStack (alignment: .center, spacing: 20){
Text("Quickly find and book a\n Doctors visit today")
.bold()
.font(.title)
.foregroundColor(.white)
.multilineTextAlignment(.center)
TextField("Email", text: $username)
.padding(.horizontal)
.foregroundColor(.white)
.accentColor(.white)
Divider()
TextField("Password", text: $password)
.padding(.horizontal)
.foregroundColor(.white)
.accentColor(.white)
Divider()
NavigationLink(destination: DetailView(), tag: 3, selection: self.$buttton1) {
EmptyView();
}
Button(action:{
self.buttton1 = 3;
print("Login");
}){
Text("Sign in")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
.cornerRadius(3)
.foregroundColor(Color.white)
}
Text("Forgot password?")
.foregroundColor(Color.white)
}
.padding(20)
HStack {
VStack { Divider().background(Color.white) }
Text("or")
.foregroundColor(Color.white)
VStack { Divider().background(Color.white) }
}
.padding(20)
VStack(alignment: .leading, spacing: 20){
Button(action:{
print("Search and sign up user");
}){
Text("Search and sign up user")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.foregroundColor(Color.white)
.border(Color.white, width: 1)
.cornerRadius(3)
}
NavigationLink(destination: DetailView(), tag: 2, selection: self.$google) {
Button(action:{
self.google = 2;
print("Sign in with Google");
}){
HStack {
/*Image(systemName: "")
.font(.title)*/
Text("Sign in with Google")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.foregroundColor(Color.black)
.background(Color.white)
.cornerRadius(3)
}
}
}
/*NavigationLink(
destination: DetailView()){
Text("Click Me")
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.black)
.foregroundColor(Color.white)
}*/
NavigationLink(destination: DetailView(), tag: 1, selection: $selection) {
//EmptyView()
Button(action:{
//self.tag = 1;
self.selection = 1;
print("Sign in with Apple");
},label: {
Text("Sign in with Apple")
})
.frame(minWidth: 0, maxWidth: .infinity,minHeight: 50, maxHeight: 50)
.background(Color.black)
.cornerRadius(3)
.foregroundColor(Color.white)
}
}
.padding(20)
}
.background(Color.blue)
.edgesIgnoringSafeArea(.all)
}
}
}
struct ContentView: View {
#State var username: String = ""
#State var password: String = ""
#State var selection: Int? = nil
#State var google: Int? = 0
#State var buttton1: Int? = nil
var body: some View {
NavigationView{
VStack{
Spacer()
VStack (alignment: .center, spacing: 20){
Text("Quickly find and book a\n Doctors visit today")
.bold()
.font(.title)
.foregroundColor(.white)
.multilineTextAlignment(.center)
TextField("Email", text: $username)
.padding(.horizontal)
.foregroundColor(.white)
.accentColor(.white)
Divider()
TextField("Password", text: $password)
.padding(.horizontal)
.foregroundColor(.white)
.accentColor(.white)
Divider()
NavigationLink(destination: DetailView(), tag: 3, selection: self.$buttton1) {
EmptyView();
}
Button(action:{
self.buttton1 = 3;
print("Login");
}){
Text("Sign in")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.green)
.cornerRadius(3)
.foregroundColor(Color.white)
}
Text("Forgot password?")
.foregroundColor(Color.white)
}
.padding(20)
HStack {
VStack { Divider().background(Color.white) }
Text("or")
.foregroundColor(Color.white)
VStack { Divider().background(Color.white) }
}
.padding(20)
VStack(alignment: .leading, spacing: 20){
Button(action:{
print("Search and sign up user");
}){
Text("Search and sign up user")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.foregroundColor(Color.white)
.border(Color.white, width: 1)
.cornerRadius(3)
}
NavigationLink(destination: DetailView(), tag: 2, selection: self.$google) {
Button(action:{
self.google = 2;
print("Sign in with Google");
}){
HStack {
/*Image(systemName: "")
.font(.title)*/
Text("Sign in with Google")
.padding()
.frame(minWidth: 0, maxWidth: .infinity)
.foregroundColor(Color.black)
.background(Color.white)
.cornerRadius(3)
}
}
}
/*NavigationLink(
destination: DetailView()){
Text("Click Me")
.frame(minWidth: 0, maxWidth: .infinity)
.background(Color.black)
.foregroundColor(Color.white)
}*/
NavigationLink(destination: DetailView(), tag: 1, selection: $selection) {
//EmptyView()
Button(action:{
//self.tag = 1;
self.selection = 1;
print("Sign in with Apple");
},label: {
Text("Sign in with Apple")
})
.frame(minWidth: 0, maxWidth: .infinity,minHeight: 50, maxHeight: 50)
.background(Color.black)
.cornerRadius(3)
.foregroundColor(Color.white)
}
}
.padding(20)
}
.background(Color.blue)
.edgesIgnoringSafeArea(.all)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Known bug on XCode and Simulators with SwiftUI that actually works just fine on real devices.
You cannot push twice in a row the same view.
For example, let's say you have 3 rows (#1, #2, #3), each with a NavigationLink, you will not be able to push #1, come back and push #1 again.
If you run #1, #2 and then, #1 again, it works.
Again, it is just on Simulator. Try the same code on a real device, it will be good!
Hope this helps!
UPDATE: The bug is fixed in XCode 11.4. You can now navigate multiple times to the same View! Good job Apple ;)