I have a navigation view with tab views inside. The reason for this arrangement is to make the tab views disappear when within the navigation views.
I am trying to place a button at the top right corner of the screen (outside the safe area). If EntryView is called directly, the view correctly places the button ("Save") in the corner without use of offset() or position() view modifiers.
When the tab view is called before EntryView, the Save button appears an inch or so below where I would like it to appear. With offset() or position() I can move the button text back where I would like it to appear, but the tappable area doesn't move to the new location.
I have tried different arrangements of ZStack and VStack, but the arrangement below is the closest I have come to getting the button to work in the upper right corner.
Here is where I would like the button to appear: https://i.stack.imgur.com/AMdKQ.png
Is there any way to move the tappable area up to where the text is located?
Or is there some better way to draw the top part of the view?
This code can be dropped directly into Xcode for analysis.
struct ContentView: View {
#State private var selectedTab: Tabs = .home
var body: some View {
NavigationView {
TabView (selection: $selectedTab) {
EntryView()
.tabItem {
Label("Home", systemImage: "house.circle.fill")
}.tag(Tabs.home)
HistoryView()
.tabItem {
Label("History", systemImage: "clock.fill")
}.tag(Tabs.history)
}
}
}
}
struct EntryView: View {
var body: some View {
GeometryReader { g in
ZStack (alignment: .top) {
Color.blue
.frame(height: (g.safeAreaInsets.top) * 0.6, alignment: .top)
.ignoresSafeArea()
HStack {
Spacer()
Button(action: {
print("Save tapped!")
}) {
Text("Save")
.font(Font.title3.bold())
.foregroundColor(.red)
.offset(y: g.size.height * -0.14)
// .position(x: g.size.width * 0.90, y: g.size.height * -0.115)
}
}
.padding(.trailing, 10)
}
}
}
}
struct HistoryView: View {
var body: some View {
VStack (alignment: .leading) {
Text("History Tab")
.padding(.top)
}
}
}
enum Tabs: String {
case home
case history
}
Related
This is probably a custom view but in the Reddit app there's a toolbar and the top left button(3 lines) opens this kind of view from the side that moves the current view to the right so you can only see about 25% of it and a new view that takes up about 75% of the screen slides in. Is there anything like this built into SwiftUI and if there isn't how would I go about implementing something like this?
This is my custom side bar behave similarly to what you just mentioned, you can try it. (Images and Code are below)
Before click:
After clicked:
struct ContentView: View {
#State var isClicked = false
var body: some View {
HStack {
Rectangle()
.fill(.orange)
.frame(width: isClicked ? UIScreen.main.bounds.width * 0.75 : 0)
VStack {
HStack {
Button {
withAnimation {
isClicked.toggle()
}
} label: {
Image(systemName: "menucard.fill")
.padding(.leading)
}
Spacer()
}
Spacer()
}
}
}
}
How can I implement play buttons that navigate to different views in the corner of each music category item? Here is an image of what I am looking for:
Here is my code:
struct ScrollCategories: View {
var body: some View {
ZStack {
ScrollView {
LazyVGrid (columns: [GridItem(.fixed(200)), GridItem(.fixed(200))]) {
ForEach(musics) { sub in
ZStack {
VStack(alignment: .leading) {
//Hidden Code
} // END OF VSTACK
NavigationLink {
// Naviagte to a different view!
Onboarding()
} label: {
Image(systemName: "play.circle")
.font(.system(size: 30))
.padding(.leading, -72.0)
.padding(.bottom, 130.0)
}
} // END OF ZSTACK
}// END OF FOR EACH
} // END OF GRID ITEM
} // END OF SCROLL VIEW
} //END OF ZSTACK
}
}
As you can see, I have a navigation link, yet it does not show in the preview or simulator.
Here is an image of what the preview looks like:
Your navigation link is there. You just use padding too high that it was out of the frame. You can use ZStack with alignment to put on the left-top corner and add padding to make it nice.
ZStack(alignment: .topLeading) { <- positioning for each View
VStack(alignment: .leading) {
//Hidden Code
} // END OF VSTACK// END OF VSTACk
NavigationLink {
// Navigate to a different view!
Onboarding()
} label: {
Image(systemName: "play.circle")
.font(.system(size: 30))
.padding( {{ put your desire padding }})
}
} // END OF ZSTACK
I am still learning SwiftUI and I have come across a small problem.
In my app, I have a main view. On the top is a search bar and at the bottom, a menu with different buttons. I want to change views when clicking those buttons. However, I only want to change the middle section.
No big deal, I will just put the middle part into a NavigationView. That works alright and I am able to change my views. My problem is that the buttons below do not have any impact on the new view.
To try to simplify: Let’s say I’m on home page. I then click the grocery list button (guess what I’m making school projects lol). My navigation link works just fine and goes to the list. So, now I’m on view 2 let’s say. When I press the home button, it doesn’t close that view and go to my main one. Here is my code setup:
import SwiftUI
struct ContentView: View {
#State private var searchText: String = ""
#State private var action: Int? = 0
var body: some View {
ZStack {
// Top Menu
VStack{
HStack {
Spacer()
TextField("Search",
text: $searchText)
.background(Color.white)
Button(action: {
self.action = 1
}, label: {
Image(systemName: "magnifyingglass.circle")
.font(.largeTitle)
})
Spacer()
}
// Body
NavigationView {
VStack {
Text("Can I See Something")
NavigationLink(destination: SearchView(), tag: 1, selection: $action) {
}
Text("Yes/No")
}
}
Spacer()
// Bottom Menu
HStack (alignment: .top) {
Spacer()
VStack {
Button(action: {
}, label: {
Image(systemName: "house.fill")
.font(.largeTitle)
})
.padding(.top)
Text("Home")
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "newspaper")
.font(.largeTitle)
})
.padding(.top)
Text("Weekly\nAd")
.multilineTextAlignment(.center)
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "checklist")
.font(.largeTitle)
})
.padding(.top)
Text("Grocery\nList")
.multilineTextAlignment(.center)
}
Divider()
.padding(.horizontal)
.frame(width: 2.5, height: 100)
VStack {
Button(action: {
}, label: {
Image(systemName: "person.crop.circle")
.font(.largeTitle)
})
.padding(.top)
Text("Account")
}
Spacer()
}
}
}
}
}
struct SearchView: View {
var body: some View {
ZStack {
Text("Nothing to see here!")
}
.navigationBarBackButtonHidden(true)
}
}
SearchView is a separate view (in its own file) in the app that opens up when the magnifying glass button is pressed. Currently it does not do anything. However I want to be able to press those buttons on this view above to still navigate the app.
Also, on another note, is there anyway to get rid of the back button?
In your code the buttons do not have any function.
Instead of creating a tab bar on your own, I'd rather take something like:
import SwiftUI
struct ContentView: View {
var body: some View {
TabView {
MainView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
NewsView()
.tabItem {
Label("Weekly\nAd", systemImage: "newspaper")
}
OrderView()
.tabItem {
Label("Grocery\nList", systemImage: "checklist")
}
AccountView()
.tabItem {
Label("Account", systemImage: "person.crop.circle")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct MainView: View {
var body: some View {
Text("Home View")
}
}
struct NewsView: View {
var body: some View {
Text("News View")
}
}
struct OrderView: View {
var body: some View {
Text("Order View")
}
}
struct AccountView: View {
var body: some View {
Text("Account View")
}
}
In that case you'll have to create a view for each tab you are configuring (see the last 4 structs).
If you want to do it with a Stack with your own created buttons, I think you should create al 4 views as well and then you either hide them or put them out of focus by using an offset. In that case the buttons should hide/show the specific views or change the offset accordingly to move the specific views into the visible area. With the offset you also can add some animation.
Regarding the search bar on top of your app, since the views are all different, I wouldn't keep the same search bar everywhere, but if you really want to have it that way, you can embed the code + your search bar into a VStack (as you did it in your example).
I'm having trouble centering that second "Date" Text within the HStack. As you can see, in the image, it is a bit farther to the left. I want only the second view to be centered in the HStack. I want the first View to be latched to leading.
Here is the code.
import SwiftUI
struct DaySummariesBarChart: View {
var body: some View {
HStack {
Text("Date")
.font(.footnote)
Text("Date")
Spacer()
}
}
}
struct BarChart_Previews: PreviewProvider {
static var previews: some View {
DaySummariesBarChart()
.previewLayout(.sizeThatFits)
}
}
This is a pretty clean way to do it:
var body: some View {
ZStack {
Text("Date")
.font(.footnote)
.frame(maxWidth: .infinity, alignment: .leading)
Text("Date")
}
}
The first Text gets a maxWidth of infinity, so it takes up the whole space, but is aligned to .leading.
The second Text is centered by default in the ZStack.
The Spacer() moves the Views to the left. Your problem should be solved by adding another Spacer() on the other side.
struct DaySummariesBarChart: View {
var body: some View {
HStack {
Spacer()
Text("Date")
.font(.footnote)
Text("Date")
Spacer()
}
}
}
You can use a GeometryReader to make the width of the Views exactly half and therefore center the second one.
struct DaySummariesBarChart: View {
var body: some View {
GeometryReader { geometry in
HStack {
Text("Date")
.font(.footnote)
.frame(width: geometry.size.width / 2)
Text("Date")
.frame(width: geometry.size.width / 2)
}
}
}
}
I have a small example that U do not understand:
Why is the alignment of my ZStack not applied to all of it's children? The TopView stays on top but I would expect, that every child would be on bottom right:
struct ContentView: View {
var body: some View {
ZStack(alignment: .bottomTrailing) {
VStack {
TopView()
Spacer()
}
Text("A new layer")
}.padding()
}
}
struct TopView: View {
var body: some View {
HStack(alignment: .top) {
VStack {
Text("SwiftUI")
Text("Layout")
}
Spacer()
Image(systemName: "star")
}
}
}
By default all the views are in the middle of area they need. Even you're using VStack it will be in the middle of the screen and be with height of two Text (as in your example).
ZStack has the same behavior - it will be as small as possible and right in the center of safe area.
Spacer just under TopView tries to take all the free space. Just as Spacer between star and texts.
So VStack in your ZStack technically is on .bottomTrailing, but takes all the free space. Just try to remove or change position of Spacer's: