how can i navigate from UIAlertController to a new screen (swiftUI) - swift

how can I navigate from UIAlertController(UIKit) to a new view(SwiftUi)
due the the reason the swiftui alert can't use a textfield
I used UIKit(UIalertcontroller)
and I want to open a new screen after I click the submit button
I have tried:
var passwordField: UITextField? = nil
let alert = UIAlertController(title: "Admin Passcode", message: "Please enter an administrator password", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Submit", style: .default, handler: { Action in
if let passwordField = passwordField {
if passwordField.text == Constants.AdminPwd {
print("Wow😁")
DeviceConfigurationView(activeView: true)
} else {
let alert2 = UIAlertController(title: "Bad Password", message: "Incorrect password", preferredStyle: .alert)
alert2.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
rootContoller().present(alert2, animated: true, completion: nil)
}
}
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .default) {
action in
alert.dismiss(animated: true)
})
alert.addTextField {
(textField) in
textField.placeholder = "Password"
textField.isSecureTextEntry = true
textField.becomeFirstResponder()
passwordField = textField
}
rootContoller().present(alert, animated: true, completion: nil)
func rootContoller()->UIViewController{
guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return .init()
}
guard let root = screen.windows.first?.rootViewController else {
return .init()
}
return root
}
}
this is the view I want to navigate to:
import UIKit
struct DeviceConfigurationView: View {
#State var activeView: Bool
var body: some View {
NavigationView {
DeviceView()
}
.navigationViewStyle(.stack)
}
}
struct DeviceView: View {
#Environment(\.presentationMode) var presentationMode
#State var ToggleIsOn: Bool = false
#State var isExpandAudioSampleRate: Bool = false
#State var isExpandVideoResolution: Bool = false
#State var isExpandVideoResolutionForLocalVideoFile: Bool = false
#State private var textField: String = ""
var body: some View {
List {
// Device
Section(header:Text("Device"),
footer:
HStack {
Text("Device ID")
TextField("Device id", text: $textField)
}) {
}
// offline
Section {
Toggle(isOn: $ToggleIsOn) {
Text("Offline Mode")
.opacity(5)
}
} header: {
Text("Offline")
}
// manage Recordings
Section {
Button {
// action
} label: {
Text("Delete All Recordings")
.foregroundColor(Color.Theme.redcoolor)
}
} header: {
Text("Manage Recordings")
}
// Firebase
Section {
HStack {
Text("Crash testing")
Spacer()
Button {
// action
} label: {
Text("Crash")
.foregroundColor(Color.blue)
}
}
} header: {
Text("Firebase")
}
Section {
HStack{
Text("Audio SampleRate")
.bold()
Spacer()
Button {
isExpandAudioSampleRate.toggle()
} label: {
Image(systemName: isExpandAudioSampleRate ? "chevron.up" : "chevron.down")
.foregroundColor(Color.blue)
.font(.system(size: 30))
}
}
}
Section {
HStack{
Text("Video Resolution")
.bold()
Spacer()
Button {
isExpandVideoResolution.toggle()
} label: {
Image(systemName: isExpandVideoResolution ? "chevron.up" : "chevron.down")
.foregroundColor(Color.blue)
.font(.system(size: 30))
}
}
}
Section {
HStack{
Text("Video Resolution For Local Video File")
.bold()
Spacer()
Button {
isExpandVideoResolutionForLocalVideoFile.toggle()
} label: {
Image(systemName: isExpandVideoResolutionForLocalVideoFile ? "chevron.up" : "chevron.down")
.foregroundColor(Color.blue)
.font(.system(size: 30))
}
}
}
}
.listStyle(PlainListStyle())
.navigationTitle("Device Configuration")
.navigationBarItems(
leading: Button(action: {
presentationMode.wrappedValue.dismiss()
print("Cancelled")
}, label: {
Text("Cancel")
}),
trailing: Button(action: {
// MainView()
print("Saved")
}, label: {
Text("Save")
}))
}
}
and I am getting this error
my error

From your code logic, it seems to be like
if passwordField.text == Constants.AdminPwd {
print("Wow😁")
let controller = UIHostingController(rootView: DeviceConfigurationView(activeView: true))
rootContoller().present(controller, animated: true, completion: nil)
} else {
...
}

Related

Why I am not able to use multiple `.alert` dialog in my SwiftUI project?

I want to delete list items and when I delete list items, it will show confirmation dialog like .alert dialog. I have code below and if I want to remove list item .alert dialog is work, but if I try to remove all list items, .alert dialog not work, and I am not able to remove all items, I do not know where I missed? I guess most probably it is due to the I have two .alert dialog and they are conflicted, any idea?
struct CustomView: View {
#State private var selectedUsers: CustomModel?
#State var users: [CustomModel]
#State private var selectDelete = false
#State private var selectAllDelete = false
var body: some View {
ScrollView(.vertical, showsIndicators: false, content: {
VStack(content: {
ForEach(users){ user in
CustomRowView(user: user)
.contextMenu {
Button(action: {
selectDelete = true
}) {
Text("remove")
}
Button(action: {
selectAllDelete = true
}) {
Text("remove all")
}
}
.alert(isPresented: $selectDelete) {
Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.delete(item: data)
},
secondaryButton: .cancel()
)
}
.alert(isPresented: $selectAllDelete) {
Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.datas.removeAll()
},
secondaryButton: .cancel()
)
}
.onDelete { (indexSet) in
self.users.remove(atOffsets: indexSet)
}
}
})
})
}
private func delete(item user: CustomModel) {
if let index = users.firstIndex(where: { $0.id == user.id }) {
users.remove(at: index)
}
}
}
model:
struct CustomModel: Identifiable{
var id = UUID().uuidString
var name: String
}
var users = [
CustomModel(name: "david"),
CustomModel(name: "marry"),
CustomModel(name: "henry"),
CustomModel(name: "nadi"), ]
You can create an alert type and handle it using switch statement.
enum AlertType {
case selectDelete
case selectAllDelete
}
private var alertType: AlertType?
#State private var isAlertPresented = false
...
Button(action: {
alertType = .selectDelete
isAlertPresented = true
}) {
Text("remove all")
}
...
.alert(isPresented: $isAlertPresented) {
presentAlert()
}
...
func presentAlert() -> Alert {
switch alertType {
case .selectDelete:
return Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.delete(item: data)
},
secondaryButton: .cancel())
case .selectAllDelete:
return Alert(title: Text("title"),
message: Text("message"),
primaryButton: .destructive(Text("Delete")) {
self.datas.removeAll()
},
secondaryButton: .cancel())
default:
return Alert(title: Text(""))
}
}
If you apply the modifier to each Button it'll work. Also, you might find confirmationDialog more suitable for this task.
Move your Buttons into custom Views will help too because body has a 10 View limit.

More than 1 alert in SwiftUI [duplicate]

I want to immediately present the second alert view after click the dismiss button of the first alert view.
Button(action: {
self.alertIsVisible = true
}) {
Text("Hit Me!")
}
.alert(isPresented: $alertIsVisible) { () -> Alert in
return Alert(title: Text("\(title)"), message: Text("\n"), dismissButton:.default(Text("Next Round"), action: {
if self.score == 100 {
self.bonusAlertIsVisible = true
}
.alert(isPresented: $bonusAlertIsVisible) {
Alert(title: Text("Bonus"), message: Text("You've earned 100 points bonus!!"), dismissButton: .default(Text("Close")))}
})
)
However, it gives me an error of 'Alert.Button' is not convertible to 'Alert.Button?'
If I put this segment out of the scope of dismissButton, it will override the previous .alert.
So how can i do it, I just want to pop up the second alert after clicking the dismiss button of the first alert.
Thanks.
It appears (tested with Xcode 11.2):
While not documented, but it is not allowed to add more than one
.alert modifier in one view builder sequence - works only latest
It is not allowed to add .alert modifier to EmptyView, it does not work
at all
I've found alternate solution to proposed by #Rohit. In some situations, many alerts, this might result in simpler code.
struct TestTwoAlerts: View {
#State var alertIsVisible = false
#State var bonusAlertIsVisible = false
var score = 100
var title = "First alert"
var body: some View {
VStack {
Button(action: {
self.alertIsVisible = true
}) {
Text("Hit Me!")
}
.alert(isPresented: $alertIsVisible) {
Alert(title: Text("\(title)"), message: Text("\n"), dismissButton:.default(Text("Next Round"), action: {
if self.score == 100 {
DispatchQueue.main.async { // !! This part important !!
self.bonusAlertIsVisible = true
}
}
}))
}
Text("")
.alert(isPresented: $bonusAlertIsVisible) {
Alert(title: Text("Bonus"), message: Text("You've earned 100 points bonus!!"), dismissButton: .default(Text("Close")))
}
}
}
}
Please try below code.
Consecutively present two alert views using SwiftUI
struct ContentView: View {
#State var showAlert: Bool = false
#State var alertIsVisible: Bool = false
#State var bonusAlertIsVisible: Bool = false
var body: some View {
NavigationView {
Button(action: {
self.displayAlert()
}) {
Text("Hit Me!")
}
.alert(isPresented: $showAlert) { () -> Alert in
if alertIsVisible {
return Alert(title: Text("First alert"), message: Text("\n"), dismissButton:.default(Text("Next Round"), action: {
DispatchQueue.main.async {
self.displayAlert()
}
})
)
}
else {
return Alert(title: Text("Bonus"), message: Text("You've earned 100 points bonus!!"), dismissButton:.default(Text("Close"), action: {
self.showAlert = false
self.bonusAlertIsVisible = false
self.alertIsVisible = false
})
)
}
}
.navigationBarTitle(Text("Alert"))
}
}
func displayAlert() {
self.showAlert = true
if self.alertIsVisible == false {
self.alertIsVisible = true
self.bonusAlertIsVisible = false
}
else {
self.alertIsVisible = false
self.bonusAlertIsVisible = true
}
}
}

ActionSheet/Sheet to Modal in SwiftUI

I am trying to create an actionSheet that opens a modal to save to a view. Currently, I can only get one of the buttons in the actionSheet to open to the modal. How can I rewrite the .sheet code to do this? Thanks.
#State var showActionSheet = false
#State var showAddActivityPopup = false
#State var showAddDayPopup = false
var body: some View {
Button(action: {
self.showActionSheet = true
}) {
Text("Show Action Sheet")
}
.actionSheet(isPresented: $showActionSheet, content: actionSheet)
.sheet(isPresented: $showAddDayPopup, onDismiss: {
print(self.showActionSheet)
}) {
AddDayPopup(message: "Add Day")
}
.sheet(isPresented: $showAddActivityPopup, onDismiss: {
print(self.showActionSheet)
}) {
AddActivityPopup(message: "Add Activity")
}
}
private func actionSheet() -> ActionSheet {
let button1 = ActionSheet.Button.default(Text("Add Day")) {
self.showActionSheet = false
self.showAddDayPopup = true
}
let button2 = ActionSheet.Button.default(Text("Add Activity")) {
self.showActionSheet = false
self.showAddActivityPopup = true
}
let actionSheet = ActionSheet(title: Text("What would you like to add?"),
message: nil,
buttons: [button1, button2, .cancel()])
return actionSheet
}
}

Display an Alert inside a conditional with SwiftUI

I understand that an Alert can be presented as a function of a Button, but can an Alert be presented inside a conditional? Such as:
struct ContentView: View {
var body: some View {
Text("Hello World!")
}
}
if isValid {
//present alert
let alert = UIAlertController(title: "My Title", message: "This
is my message.", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style:
UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
With this I get
Value of type 'ContentView' has no member 'present'
I'm not sure why are you using UIKit. Here's an example of how an alert may be presented when something changes a flag. In this case, a two second timer:
import SwiftUI
class MyModel: ObservableObject {
#Published var isValid: Bool = false
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
self.isValid = true
}
}
}
struct ContentView: View {
#ObservedObject var model: MyModel = MyModel()
var body: some View {
VStack {
Text("Some text")
Text("Some text")
Text("Some text")
Text("Some text")
}.alert(isPresented: $model.isValid, content: {
Alert(title: Text("Title"),
message: Text("Message"),
dismissButton: .default(Text("OK")) { print("do something") })
})
}
}

SwiftUI Change View with Button

I understand there is PresentationButton and NavigationButton in order to change views in the latest SwiftUI. However I want to do a simple operation like below. When user clicks on SignIn button if credentials are correct it will sign them in but also do a segue (in this case change the view). However I could not check if they are correct in PresentationButton and I could not change the view in a normal button.
Is there another way to do that?
#IBAction func signInClicked(_ sender: Any) {
if emailText.text != "" && passwordText.text != "" {
Auth.auth().signIn(withEmail: emailText.text!, password: passwordText.text!) { (userdata, error) in
if error != nil {
//error
} else {
performSegue(withIdentifier: "toFeedActivity", sender: nil)
}
}
} else {
//error
}
}
Here's one way.
struct AppContentView: View {
#State var signInSuccess = false
var body: some View {
return Group {
if signInSuccess {
AppHome()
}
else {
LoginFormView(signInSuccess: $signInSuccess)
}
}
}
}
struct LoginFormView : View {
#State private var userName: String = ""
#State private var password: String = ""
#State private var showError = false
#Binding var signInSuccess: Bool
var body: some View {
VStack {
HStack {
Text("User name")
TextField("type here", text: $userName)
}.padding()
HStack {
Text(" Password")
TextField("type here", text: $password)
.textContentType(.password)
}.padding()
Button(action: {
// Your auth logic
if(self.userName == self.password) {
self.signInSuccess = true
}
else {
self.showError = true
}
}) {
Text("Sign in")
}
if showError {
Text("Incorrect username/password").foregroundColor(Color.red)
}
}
}
}
struct AppHome: View {
var body: some View {
VStack {
Text("Hello freaky world!")
Text("You are signed in.")
}
}
}
I had the same need in one of my app and I've found a solution...
Basically you need to insert your main view in a NavigationView, then add an invisible NavigationLink in you view, create a #state var that controls when you want to push the view and change it's value on your login callback...
That's the code:
struct ContentView: View {
#State var showView = false
var body: some View {
NavigationView {
VStack {
Button(action: {
print("*** Login in progress... ***")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.showView = true
}
}) {
Text("Push me and go on")
}
//MARK: - NAVIGATION LINKS
NavigationLink(destination: PushedView(), isActive: $showView) {
EmptyView()
}
}
}
}
}
struct PushedView: View {
var body: some View {
Text("This is your pushed view...")
.font(.largeTitle)
.fontWeight(.heavy)
}
}
Try with state & .sheet
struct ContentView: View {
#State var showingDetail = false
var body: some View {
Button(action: {
self.showingDetail.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $showingDetail) {
DetailView()
}
}
}
You can use navigation link with tags so,
Here is the code:
first of all, declare tag var
#State var tag : Int? = nil
then create your button view:
Button("Log In", action: {
Auth.auth().signIn(withEmail: self.email, password: self.password, completion: { (user, error) in
if error == nil {
self.tag = 1
print("success")
}else{
print(error!.localizedDescription)
}
})
So when log in success tag will become 1 and when tag will become 1 your navigation link will get executed
Navigation Link code:
NavigationLink(destination: HomeView(), tag: 1, selection: $tag) {
EmptyView()
}.disabled(true)
if you are using Form use .disabled because here the empty view will be visible on form and you don't want your user to click on it and go to the homeView.