Navigate to new screen on button click SwiftUI - swiftui

I have a login screen and wish to navigate to a new screen when the Login button is clicked.
I did try to wrap the button and the entire screen layout under NavigationView and embedded the button in a Navigation Link.
I am unable to figure out how to show the new screen when the button is clicked. Following is the code for the login screen.
ZStack {
Color.red
.edgesIgnoringSafeArea(.all)
VStack(alignment: .center, spacing: 180.0) {
Text("SwiftUI")
.font(.largeTitle)
.bold()
.padding()
VStack(alignment: .center, spacing: 25) {
TextField("Username", text: $userName)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
TextField("Password", text: $userPassword)
.padding(.all)
.background(Color.white)
.cornerRadius(10)
Toggle(isOn: $isFirstTimeUser) {
Text("First Time User")
.font(.headline)
.bold()
.padding(.horizontal, -10)
.foregroundColor(Color.white)
}.padding(.horizontal, 17)
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
}
})
{
Text(isFirstTimeUser ? "SignUp" : "Login")
.fontWeight(.medium)
.font(.title)
.foregroundColor(Color.red)
.padding(.horizontal, 10)
}.padding()
.background(Color.white)
.cornerRadius(10)
.alert(isPresented: $isAlertShown) {
() -> Alert in
Alert(title: Text("UserName Invalid"), message: Text("Username has to be more than 5 characters"), dismissButton:.default(Text("Got that!")))
}
}.padding(.horizontal, 17)
}
}

Here is possible approach
1) Add link tag state variable to your View
#State private var current: Int? = nil
2) Wrap your view hierarchy into NavigationView to make possible NavigationLink to work
3) Add tagged NavigationLink above your button
NavigationLink(destination: YourDestinationViewHere(), tag: 1, selection: $current) {
EmptyView()
}
4) Add link tag selection to button action
Button(action: {
if self.userName.count <= 5 {
self.isAlertShown = true
} else {
self.current = 1 // this activates NavigationLink with specified tag
}
})

Related

Set View Picker Header Color?

Is it possible to add background color to the top of a picker view? I'm specifically interested in the area from the top of the view down to just below the navigation bar. I have made all my app tab and navigation views with the same header appearance but haven't had any luck with the pickers.
I would like to set the picker backgrounds to look like this:
Here is a picker view that I would like to add background color.
Here is what happens when adding call to DrawNavBox() just after the Picker (See code below). I need help pushing the green block to the top of the view.
The code below is what generates the Home Currency green header above
var body: some View {
GeometryReader { g in
VStack {
DrawNavBox(g: g) // draw green header
Text("Your home currency is:")
.padding(.top, g.size.height * 0.10)
Text("\(base.baseCur.baseCurrency) \(base.baseCur.baseCountry)")
.font(.body)
.fontWeight(.bold)
.foregroundColor(.white)
.padding(15)
.background(Color("Dark Green"))
.cornerRadius(20)
.padding(.trailing, g.size.width * 0.19)
.padding(.leading, g.size.width * 0.22)
Form {
Section(header: Text("Select Home Currency")) {
Picker("Home Currency", selection: $gotBase) {
DrawNavBox(g: g)
ForEach(0 ..< currCountry.count, id: \.self) { item in
VStack(alignment: .leading) {
Text(currCountry[item])
.font(.subheadline)
.padding(.leading, 25)
HStack {
Text(currSymbol[item])
Text(currName[item])
}
.font(.caption)
.padding(.leading, 25)
}
}
}
}
}
}
.navigationBarTitle(Text("Home Currency"), displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
// search for new base currency
self.changeBaseCurrency()
}) {
Text ("Save").bold()
}
)}
}
}
This is the code I have been using to set the navigation bar area green
struct DrawNavBox: View {
var g: GeometryProxy
var body: some View {
ZStack (alignment: .top) {
Color(.systemGreen)
.frame(height: (g.safeAreaInsets.top), alignment: .top)
.ignoresSafeArea()
}
}
}

SwiftUI Context Menu

My project was using a List but I needed a ScrollView wrapped view so I changed the List to just a ForEach loop with a view and Divider in between. I noticed my Context Menu doesn't look the same as when it was in the List as in the screenshot below.
I'd like the item selected with context menu to look like the List item selected since I can no longer use List in the code, it doesn't look as pretty. Also I have to press on the image and or text to open the Context Menu unlike in a List you can select the row. The end result would be to mimic the style of the selected List item.
/// Context Menu looks terrible
VStack (alignment: .leading, spacing: 0){
ForEach(self.welcome.views.dropLast(), id: \.id) { view in
ForEach(view.views ?? [], id: \.id) { purple in
ForEach(purple.views, id: \.id) { fluffy in
VStack (alignment: .leading, spacing: 0){
if viewClass == "FeaturedHeaderView" {
Text(title)
.font(.title3)
.fontWeight(.bold)
} else {
HStack (spacing: 15){
RequestImage(Url(image), animation: nil)
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.clipped()
.cornerRadius(13.5)
VStack (alignment: .leading){
Text(name)
.font(.headline)
.fontWeight(.bold)
Text(author)
.foregroundColor(Color.secondary)
.font(.footnote)
Text(repo)
.foregroundColor(Color.secondary)
.font(.caption)
}
}
.contextMenu(ContextMenu(menuItems: {
HStack {
Button(action: {
openURL(URL(string: "URL")!)
}) {
Label("Open in Safari", systemImage: "safari.fill")
}
}
}))
}
}
Divider()
}
}
}
.padding(.all, 5)
.padding(.leading, 5)
}
/// List Context Menu looks great but can't use List in ScrollView
List {
ForEach(self.welcome.views.dropLast(), id: \.id) { view in
ForEach(view.views ?? [], id: \.id) { purple in
ForEach(purple.views.dropLast(), id: \.id) { fluffy in
VStack (alignment: .leading, spacing: 0){
if viewClass == "FeaturedHeaderView" {
Text(title)
.font(.title3)
.fontWeight(.bold)
} else {
HStack (spacing: 15){
RequestImage(Url(image), animation: nil)
.aspectRatio(contentMode: .fit)
.frame(width: 60, height: 60)
.clipped()
.cornerRadius(13.5)
VStack (alignment: .leading){
Text(name)
.font(.headline)
.fontWeight(.bold)
Text(author)
.foregroundColor(Color.secondary)
.font(.footnote)
Text(repo)
.foregroundColor(Color.secondary)
.font(.caption)
}
}
.contextMenu(ContextMenu(menuItems: {
HStack {
Button(action: {
openURL(URL(string: "URL")!)
}) {
Label("Open in Safari", systemImage: "safari.fill")
}
}
}))
}
}
}
}
}
.padding(.all, 10)
.listRowInsets(EdgeInsets())
}
The possible solution is to add background modifier above contextMenu, like
.frame(maxWidth: .infinity) // << to make it screen-wide
.background(
RoundedRectangle(cornerRadius: 12) // << tune as needed
.fill(Color(UIColor.systemBackground)) // << fill with system color
)
.contextMenu(ContextMenu(menuItems: {
Demo with some replication (used Xcode 12.4 / iOS 14.4)

How to navigate to different screens/views by tapping button which is in ForEach

I have created a dashboard with 9 buttons laid out on a lazyVGrid and a ForEach loop to display the various buttons.
(Click to expand)
Now, I want to navigate to various new screens based on the button pressed. Someone, please help me achieve this.
LazyVGrid(columns: row, spacing: 25) {
ForEach(Dashboard_Data) { data in
Button(action: {
// action
}) {
ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top)) {
VStack(alignment: .leading, spacing: 5) {
Spacer(minLength: 0)
Text(data.data)
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
HStack{
Spacer(minLength: 0)
Text(data.suggest)
.font(.system(size: 17, weight: .regular))
.lineLimit(2)
.foregroundColor(.white)
}
}.padding()
.background(Color(data.image))
.cornerRadius(20)
.shadow(color: Color.black.opacity(0.5), radius: 5, x: 0, y: 5)
Image(data.imageIcon)
.renderingMode(.template)
.foregroundColor(.white)
.font(.system(size: 25, weight: .regular))
.padding(10)
.background(Color.white.opacity(0.15))
.clipShape(Circle())
}
}
}
}
var body: some View{
LazyVGrid(columns: row, spacing: 25) {
ForEach(Dashboard_Data) { data in
NavigationLink(destination: getTheDestination(data)) {
//label/design of your button
}
}
}
}
private func getTheDestination(data:<put ur dataType of Dashboard_Data> ) -> some View {
// add some input parameter in this func to get data/informattion from your Dashboard_Data
// according to ur data return the destination view
if data.data == "your value" {
return Text( _ data.data)
}
return Text("Destination")
}

SwiftUI NavigationLink in list

I tried to do a list which have image and a navigation link inside. In iOS 14.1 everything work fine but after I update my iOS to 14.2, something break. In the list while the user click the big image there will be a action sheet pop out, while the user click a systemImage it will trigger a navigation link. However, when I update to iOS 14.2, no matter what I clicked, it will trigger the NavigationLink. Can someone explain to me why will this happened and how to solve?
Here is the sample code
struct ContentView : View {
#State var showingActionSheet = false
#State private var action: Int? = 0
var body: some View {
NavigationView {
List{
VStack(alignment: .leading){
HStack{
VStack(alignment: .leading){
Text("my data")
Text("2020/12/12")
}
}
Image("profile")
.resizable()
.aspectRatio(contentMode: .fit)
.onTapGesture(count: 1) {
self.showingActionSheet.toggle()
}
HStack{
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.action = 1
print("select comment")
}
NavigationLink(destination: Text("test"), tag: 1, selection: $action) {
EmptyView()
}
}
}
.actionSheet(isPresented: $showingActionSheet) {
//action sheet
ActionSheet(title: Text("Test"), message: Text("Select a selection"), buttons: [
.default(Text("test")) { print("test") },
.cancel()
])
}
}
}
}
}
Try the following (disabling navigation link prevents user interaction on it, but programmatically it is activated):
HStack{
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.action = 1
print("select comment")
}
NavigationLink(destination: Text("test"), tag: 1, selection: $action) {
EmptyView()
}.disabled(true) // << here !!
}
You can use isActive to trigger the navigation link.
#State var isCommentPresented = false
...
Image(systemName: "message")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 25)
.foregroundColor(.gray)
.onTapGesture {
self.isCommentPresented = true
print("select comment")
}
}
NavigationLink(destination: Text("test"), isActive:self.$isCommentPresented) {
EmptyView()
}
Using Zstack on top of all can be the reason.
Please check once without ZStack

Having trouble with having an overlay appear over other views in SwiftUI. Does anyone have any ideas?

I'm currently working on trying to implement a custom dropdown menu in SwiftUI that displays a grid of buttons (1-16) and allows you to select one of them. I am using an overlay to display the dropdown below the corresponding button, and it seems to be functioning properly except it's displaying the dropdown below all of the other elements in the view. I found another post here regarding this issue and they used a ZStack to solve it, but I haven't been able to get the same success. Does anyone have any ideas on how to fix this? Here's my code:
struct ContentView: View {
#State var showDropdown = false
#State var selected = 0
var body: some View {
ZStack {
HStack(spacing: 30) {
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { showDropdown.toggle() }) {
Text(selected == 0 ? "Omni" : String(selected))
.frame(width: 80, height: 36)
}
.zIndex(1)
.overlay(
VStack(spacing: 0) {
if self.showDropdown {
Spacer(minLength: 26)
DropdownMenu(selection: self.$selected)
} else {
EmptyView()
}
}, alignment: .topLeading
)
}
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}
}
}
.padding()
.frame(maxHeight: .infinity)
.buttonStyle(PlainButtonStyle())
}
}
and here are some images of the results:
Before Press
After Press
Thank you in advance!
The .zIndex works for view in same container, so you need something like the following (or make custom flat layout container with all such buttons at same level, then rise clicked button atop).
VStack(alignment: .leading, spacing: 10) {
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { showDropdown.toggle() }) {
Text(selected == 0 ? "Omni" : String(selected))
.frame(width: 80, height: 36)
}
.zIndex(1)
.overlay(
VStack(spacing: 0) {
if self.showDropdown {
Spacer(minLength: 26)
DropdownMenu(selection: self.$selected)
}
}, alignment: .topLeading
)
}.zIndex(showDropdown ? 1 : 0) // << this !!
HStack {
Text("Some Text")
.lineLimit(1)
Spacer()
Button(action: { }) {
Text("Omni")
.frame(width: 80, height: 36)
}
}
}.zIndex(showDropdown ? 1 : 0) // << and this !!