SwiftUI - NavigationLink is not working with a button - swiftui

I am making an app where I take two number inputs and want to show the addition result of the numbers in the second screen, when a button is clicked. I can print the result in the console, but unfortunately it seems like navigation link is not working, around the button. If I put NavigationLink around the button label instead of around the whole button, then, it goes to the second screen but button action stops working. Here is my code:
import SwiftUI
struct ContentView: View {
#State private var number1 : String = ""
#State private var number2: String = ""
#State private var isTapped:Bool = false
#State var sum : Double = 0
var body: some View {
NavigationView {
VStack{
TextField("Type first number", text: $number1)
.keyboardType(.numberPad).padding()
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color.gray).border(Color.blue,width:5)
TextField("Type second number", text: $number2)
.keyboardType(.numberPad).padding()
.textFieldStyle(RoundedBorderTextFieldStyle())
.background(Color.gray).border(Color.blue,width:5)
//this Navigationlink is not working
NavigationLink(destination: Text("\(self.sum)")) {
Button(action: {
print("I am here in the action")
self.isTapped.toggle()
UIApplication.shared.endEditing()
if let num1 = Double(self.number1), let num2 = Double(self.number2){
print("I am here")
self.sum = num1 + num2
print("\(self.sum)")
}
}) {
//If I put the Navigationlink here, button action stop working.
Text("Add Two Numbers")
.padding()
.foregroundColor(.blue)
.background( isTapped ? Color.orange : Color.gray)
.font(.title)
.border(Color.blue, width: 5)
.shadow(radius: 10)
}
}
}
}
}
}
extension UIApplication {
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Any clue? Thanks for your curiosity.

NavigationLink is itself a button, actually, so you introduce some kind of conflict. Instead you can use link just with additional tap gesture handler, like
NavigationLink(destination: Text("\(self.sum)")) {
Text("Add Two Numbers")
.padding()
.foregroundColor(.blue)
.background(isTapped ? Color.orange : Color.gray)
.font(.title)
.border(Color.blue, width: 5)
.shadow(radius: 10)
}
.simultaneousGesture(TapGesture().onEnded{
print("I am here in the action")
self.isTapped.toggle()
UIApplication.shared.endEditing()
if let num1 = Double(self.number1), let num2 = Double(self.number2){
print("I am here")
self.sum = num1 + num2
print("\(self.sum)")
}
})

Related

NavigationLink in SwiftUI not worked as expected

I'm trying to make navigation link, here I'm creating NavigationLink with isActive based on State variable isLoggedIn. But without setting isLoggedIn true getting navigating to next screen.
also, it's navigating on tap of Email Textfield which is wrong.
My expectation is it should navigate only after isLoggedIn setting to true.
struct ContentView: View {
#State private var isLoggedIn = false
#State private var email = ""
var body: some View {
NavigationView {
NavigationLink(destination: Text("Second View"), isActive: $isLoggedIn) {
VStack {
TextField("Email", text: $email)
.frame(maxWidth: .infinity, alignment: .leading)
.border(.gray, width: 1)
.foregroundColor(.blue)
Button("Send") {
isLoggedIn = true
}
}
.padding()
}
}
}
}
The expectation is wrong, NavigationLink handles user input independently (but also, additionally, can be activated programmatically).
In this scenario, to leave only programmatic activation, we need to hide navigation link, like
NavigationView {
VStack {
TextField("Email", text: $email)
.frame(maxWidth: .infinity, alignment: .leading)
.border(.gray, width: 1)
.foregroundColor(.blue)
Button("Send") {
isLoggedIn = true
}
.background(NavigationLink(destination: // << here !!
Text("Second View"), isActive: $isLoggedIn) { EmptyView() })
}
.padding()
}
Here it's working fine with this
struct MoviesListView: View {
#State var navigate = false
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Hi"), isActive: $navigate) {
Button("Add") {
navigate.toggle()
}
}
}
}
}
}

SwiftUI pop out temporary alert

I tried to do a app that pop out a temporary alert that only appear for 1 or 2 seconds. It’s something like App Store rating.
But I don’t know what this called in swiftui. Can anyone answer me?
That is just a view that is shown or hidden conditionally. Here is a complete example that uses a ZStack to place the thank you view over the other view content. The thank you view is either present or not based upon the #State variable showThankYou. DispatchQueue.main.asyncAfter is used to remove the view after 3 seconds.
struct ContentView: View {
#State private var showThankYou = false
var body: some View {
ZStack {
VStack {
Spacer()
Text("Stuff in the view")
Spacer()
Button("submit") {
showThankYou = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.showThankYou = false
}
}
Spacer()
Text("More stuff in the View")
Spacer()
}
if showThankYou {
RoundedRectangle(cornerRadius: 16)
.foregroundColor(Color.gray)
.frame(width: 250, height: 250)
.overlay(
VStack {
Text("Submitted").font(.largeTitle)
Text("Thanks for your feedback").font(.body)
}
)
}
}
}
}

SwiftUI: Have a button change itself?

I am trying to make a struct that shows a menu of radio buttons.
The issue I have is the following: when I press a button, I want the Text(item) View to change color. I'm not sure how to do that, since the Text(item) is encompassed by the button.
import SwiftUI
struct RadioMenu: View {
var items = [String]()
#State var isChecked: Bool = false
#State var selection: String? = nil
var textSize: Int = 20
init(items: [String], textSize: Int) {
self.items = items
self.textSize = textSize
}
var body: some View {
VStack {
ForEach(items, id:\.self) { item in
Button (action: {
self.isChecked = true
self.selection = item
}) {
Text(item)
.font(.system(size: CGFloat(self.textSize), weight: .medium, design: .rounded))
.padding()
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(lineWidth: 2)
)}
.padding(.bottom, 10)
}
}
}
}
You can apply this modifier on Text :
.foregroundColor(item == self.selection ? Color.red : Color.black)

How do I properly use NavigationView in a ZStack?

I am trying to add some filter options to sit at the top of my view, above the NavigationView. I wrote the following code that mostly does what I want, however it disabled the ability to click on the rows to get to the detailed view. I assume this is because my filter buttons are on top of the ZStack, but I'm not sure how else to get this to work.
Here is the code I wrote:
import SwiftUI
struct BonusList: View {
var bonuses = sampleBonusData
#State var showSettings = false
#State var showBonuses = false
#State var bonusEarned = true
#State var showStatePicker = false
#State var showCategoryPicker = false
var body: some View {
ZStack {
NavigationView {
List(bonuses) { item in
NavigationLink(destination: BonusDetail(bonusName: item.bonusName, bonusCode: item.bonusCode, city: item.city, sampleImage: item.sampleImage)) {
HStack(spacing: 12.0) {
Image(item.sampleImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 60, height: 60)
.background(Color.white)
.cornerRadius(15)
VStack(alignment: .leading) {
HStack {
Text(item.bonusName)
.font(.headline)
Spacer()
Image(systemName: "checkmark.shield")
.opacity(self.bonusEarned ? 100 : 0)
}
Text("\(item.city), \(item.state)")
.font(.subheadline)
.frame(height: 25.0)
HStack {
Text(item.bonusCategory)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.gray)
.padding(.top, 4)
Spacer()
Text(item.bonusCode)
.font(.caption)
.fontWeight(.bold)
.foregroundColor(.gray)
.padding(.top, 4)
}
}
}
}
}
.navigationBarTitle(Text("Bonuses"))
// .navigationBarHidden(true)
}
.saturation(self.bonusEarned ? 0 : 1)
HStack {
FilterByCategory(showCategoryPicker: $showCategoryPicker)
Spacer()
FilterByState(showStatePicker: $showStatePicker)
}
StatePicker(showStatePicker: $showStatePicker)
CategoryPicker(showCategoryPicker: $showCategoryPicker)
}
}
}
This is what it looks like when I run it:
If I'm understanding correctly, you have a view or two which sit higher in the ZStack that are off canvas and come in when those buttons are tapped?
You could consider using a modal and setting the view you want to show for each button as the view for the modal. This will keep your views off screen and still allow interaction with your list. Here's what I've done...
On the main view
import SwiftUI
struct MainView: View {
#State private var isPresented = false
var body: some View {
NavigationView {
VStack {
//...
}
//Modal
.sheet(isPresented: $isPresented, content: {
AddItem(showModal: self.$isPresented)
})
}
}
}
The modal's view
import SwiftUI
struct AddItem: View {
#Binding var showModal: Bool
var body: some View {
VStack {
Button(action: {
self.showModal = false
}, label: {
Text("Cancel")
})
}
}
}

SwiftUi Transition / Animation

I am new to SwiftUI and have managed to build simple game.
All works except my plan to implement some sort of image animation / transition to introduce the image into the view.
The images are formatted correctly, but too abrupt and hoped to soften the image glow
I have tried by using the animation method, but has not mad a difference.
I hope someone can point me in the right direction.
var body: some View {
NavigationView {
ZStack {
Image("background")
.resizable()
.aspectRatio(contentMode: .fill)
.scaledToFill()
.edgesIgnoringSafeArea(.all)
VStack {
Text("Tap Correct Image")
.font(.headline)
.foregroundColor(Color.blue)
ForEach((0...2), id: \.self) { number in
Image(self.naijaObject[number])
.resizable()
.frame(width: 100, height: 100)
.border(Color.black,width: 1)
.transition(.slide)
.animation(.easeInOut(duration: 1))
.onTapGesture {
self.pictureTapped(number)
}
}
.navigationBarTitle(Text(processCorrectAnswer))
.alert(isPresented: $showAlert) {
Alert(title: Text("\(alertTitle), Your score is \(score)"), dismissButton: .default(Text("Continue")) {
self.askQuestion()
})
}
}//End of VStack
}//End of
} //End of Naviagtion View
} //End of View
//Function to action which object is tapped
func pictureTapped(_ tag: Int) {
if tag == correctAnswer {
score += 1
alertTitle = "Correct"
} else {
score -= 1
alertTitle = "Wrong"
}
showAlert = true
}
//Function to continue the game
func askQuestion() {
naijaObject.shuffle()
correctAnswer = Int.random(in: 0...2)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I think I understand, what you want to achieve, but in future better to present more code. Here I used DispatchQueue.main.asyncAfter(deadline: .now() + 1) where I change the variable showAlert to true. And it works smoothly, hope it'll help you:
struct QuestionGame: View {
#State private var showAlert = false
#State private var score: Int = 0
#State private var correctAnswer = 1
#State private var tapped = false
var body: some View {
NavigationView {
VStack {
Text("Tap Correct Image")
.font(.headline)
ForEach((0...2), id: \.self) { number in
Rectangle()
.frame(width: 100, height: 100)
.foregroundColor(self.tapped && number == self.correctAnswer ? .green : .black) // you can do anything for animation
.animation(.easeInOut(duration: 1.0))
.onTapGesture {
self.pictureTapped(number)
}
}
.navigationBarTitle(Text("answer the question"))
.alert(isPresented: self.$showAlert) {
Alert(title: Text("Your score is \(score)"), dismissButton: .default(Text("Continue")) {
self.askQuestion()
})
}
}
}
}
func pictureTapped(_ tag: Int) {
tapped = true
if tag == correctAnswer {
score += 1
} else {
score -= 1
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.showAlert = true
}
}
//Function to continue the game
func askQuestion() {
tapped = false
showAlert = false
correctAnswer = Int.random(in: 0...2)
}
}
struct QuestionGame_Previews: PreviewProvider {
static var previews: some View {
QuestionGame()
}
}
P.S. and no, I didn't find the way to show alert smoothly. For this case may be better to use ZStack and change the alert (or any other view) opacity from 0 to 1 (with animation), when you tap on image.