How can I display an alert dialog box from a menu item for MacOS apps using SwiftUI?
The usual code which works for iOS #State var isOn = false and .alert("title", isPresented: isOn) {..} doesn't work.
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.commands {
CommandMenu("Test menu") {
Button(action: {
// I want to show an alert dialog dialog here.
}) {
Text("Click Me")
}
}
}
}
The usual code works fine. You would never try to stuff an Alert inside of a Button. You wouldn't do it here.
#main
struct MyApp: App {
#State var isOn = false
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.alert("title", isPresented: $isOn) {
Button("OK", role: .cancel) { }
}
}
}.commands {
CommandMenu("Test menu") {
Button(action: {
isOn = true
}) {
Text("Click Me")
}
}
}
}
}
Related
Here is the code fragment from Apple Document
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
#if os(macOS)
Settings {
SettingsView()
}
#endif
}
}
I followed the document to watch the scenePhase like this but got nothing
struct MyScene: Scene {
#Environment(\.scenePhase) private var scenePhase
var body: some Scene {
Settings {
SettingView()
}
.onChange(of: scenePhase) { newValue in
print(newValue)
}
}
}
I think that is because that scenePhase is the scenePhase of MyApp's Scene. But how to get the scenePhase of Settings? Or how to detect SettingView's on disappear?
Codes below are not working.
struct SettingView: View {
#Environment(\.scenePhase) private var scenePhase
var body: some View {
SomeView()
.onAppear(perform: {
print("setting view on appear") // only print once
})
.onDisappear {
print("on disappear") // never print
}
.onChange(of: scenePhase) { newValue in
print(newValue) // only print once
}
}
}
So basically in my SwiftUI app, there is a login screen and if you are already logged in then you move on to the HomeView(). If not, then you stay on the LoginView(). However, every time I open the app, the .fullScreenCover flickers before the .onAppear{} statement realizes it's time for the cover to disappear. Here is the code:
struct HomeView: View {
#ObservedObject var fireViewModel = FirebaseViewModel()
#State var loginPresented = true
var body: some View {
ZStack {
VStack {
Text("You are already Signed in")
Button(action: {
fireViewModel.signOut()
}, label: {
Text("Sign Out")
})
}
}
.onAppear {
if fireViewModel.signedIn {
loginPresented = true
} else {
loginPresented = false
}
}
.fullScreenCover(isPresented: $loginPresented, onDismiss: nil, content: {
LoginView()
})
}
}
Setting the
#State var loginPresented = false
In the beginning solves the problem.
Does someone know how to connect commands to the rest of the project?
For example: I want to toggle the AddNew variable in the content view to show the add new item sheet by using the command.
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandGroup(after: CommandGroupPlacement.newItem) {
Button("Add new", action: {
self.AddNew.toggle() // should toggle variable in content View
})
}
}
}
}
struct ContentView: View {
#State var AddNew = false
var body: some View {
Button(action: {
self.AddNew.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $AddNew) {
AddNew(dimiss: $AddNew)
}
}
}
A solution could be to have a #Published var in a class conforming to ObservableObject.
You would toggle the boolean in the class and access it from wherever you want (as an #EnvironmentObject for example).
Like this:
class AppModel: ObservableObject {
#Published var addNew: Bool = false
}
struct SampleApp: App {
#ObservedObject var model = AppModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
}
.commands {
CommandGroup(after: CommandGroupPlacement.newItem) {
Button("Add new", action: {
self.model.addNew.toggle()
})
}
}
}
}
struct ContentView: View {
#EnvironmentObject var model: AppModel
var body: some View {
Button(action: {
self.model.addNew.toggle()
}) {
Text("Show Detail")
}.sheet(isPresented: $model.addNew) {
AddNew(dimiss: $model.addNew)
}
}
}
How can I change the Color from the Button in a Alert and the back Button from NavigationLink? To set .accentColor(.init("someColor")) after the Text from the Alert Button doesn't work. To set .accentColor(.init("someColor")) after navigationLink does not work too. What can I do?
The Alert Button:
The Back Button from NavigationLink:
struct ContentView: View {
#State var showSheet = false
#State var alertShouldBeShown = !UserDefaults.standard.bool(forKey: "£Start")
var body: some View {
Button(action: {
self.showSheet.toggle()
}) {
Text("click me")
//.accentColor(colorScheme == .dark ? Image("") : Image("Info-Icon"))
}.sheet(isPresented: $showSheet) {
SheetView(showSheet: self.$showSheet)
}
.alert(isPresented: $alertShouldBeShown, content: {
Alert(title: Text("Headline"),
message: Text("Text"),
dismissButton: Alert.Button.default(
Text("Ok"), action: {
UserDefaults.standard.set(true, forKey: "Start")
}
)
)
})
}
}
struct SheetView: View {
#Binding var showSheet: Bool
var body: some View {
NavigationView {
DetailView()
.navigationBarTitle(Text("Headline"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.showSheet = false
}) {
Text("Done")
.bold()
})
}.navigationViewStyle(StackNavigationViewStyle())
}
}
struct DetailView: View {
var body: some View {
List {
HStack {
NavigationLink(destination: DetailViewOne()) {
Text("View 1")
}
}
HStack {
NavigationLink(destination: DetailViewTwo()) {
Text("View 2")
}
}
}
}
}
struct DetailViewOne: View {
var body: some View {
Text("1")
}
}
struct DetailViewTwo: View {
var body: some View {
Text("2")
}
}
You can change the NavigationBar accent color by using accentColor but you need to apply it to the SheetView (the root view of a given environment):
SheetView(showSheet: self.$showSheet)
.accentColor(.red)
Unfortunately SwiftUI doesn't allow much of Alert customisation so far.
However, as Alert is built on top of UIKit components (UIAlertController) this also means you can change the appearance of UIView when contained in UIAlertController.
You can put this code in your #main App init or in the SceneDelegate:
UIView.appearance(whenContainedInInstancesOf: [UIAlertController.self]).tintColor = .red
I´m new in SwiftUI and I have the following problem. When I click the button on the iPhone the following comes up
but when I press the button on the iPad the following comes up
The picker is not on the whole sheet. How can I show the picker on the iPad on the whole sheet like on the iPhone?
struct ContentView: View {
#State var value_2 = 1
#State var show_1 = false
var body: some View {
VStack {
Button(action: {
self.show_1.toggle()
}) {
Text("Push")
}
.sheet(isPresented: $show_1) {
Sheet(show_0: self.$show_1, value_1: self.$value_2)
}
}
}
}
struct Sheet: View {
#Binding var show_0: Bool
#Binding var value_1: Int
var body: some View {
NavigationView {
number(value_0: $value_1)
.navigationBarTitle(Text("Enter number"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.show_0 = false
}) {
Text("Done").bold()
})
}
}
}
struct number: View {
#Binding var value_0: Int
var body: some View {
Section {
Text("Headline")
Picker("",selection: $value_0)
{
ForEach(1..<101) { value in
Text("\(value)")
}
}
}
.labelsHidden()
}
}
As far as I understood your intention, you need just change a style of navigation view, like
var body: some View {
NavigationView {
number(value_0: $value_1)
.navigationBarTitle(Text("Enter number"), displayMode: .inline)
.navigationBarItems(trailing: Button(action: {
self.show_0 = false
}) {
Text("Done").bold()
})
}
.navigationViewStyle(StackNavigationViewStyle()) // << here !!
}