Event when SwiftUi Menu is opened - swiftui

Is there any way to know when Menu control in SwiftUI is about to open OR open? I am sharing Menu code from implementation.
struct ContentView: View {
var body: some View {
Menu("Options") {
Button("Order Now", action: placeOrder)
Button("Adjust Order", action: adjustOrder)
Button("Cancel", action: cancelOrder)
}
}
}

Related

Unable to RE-navigate through SwiftUI app

I’m writing a workout app for Apple Watch but am running into issues with navigation. I have a version which includes the behaviour I’m looking for but am trying to simplify the navigation through the app which is leading to issues.
The “working” version of the app (code below) has the user select a workout from a launch page. When the workout is started, a tabbed view is displayed which contains controls on one page and metrics on another, cut down here for the purpose of demonstration. When End on the controls page is pressed, a boolean is set to true forcing a summary view to be displayed. When Done is pressed here, the view is dismissed returning the user to the launch page. A new workout can then be initiated.
As mentioned above, I’m trying to simplify the app, doing away with the workout selection and immediately leading to a start page. From there the functionality is basically the same. However, when I return to the start page, I am unable to initiate another workout without re-launching the app.
I’m just starting out on my SwiftUI journey so can only presume I’m misusing the NavigationStack or misunderstanding the concepts behind it but have not been able to see the issue.
Any assistance would be greatly appreciated.
Thanks much.
struct ContentView: View {
#StateObject private var workoutManager = WorkoutManager()
var body: some View {
NavigationStack {
List(Workout.workouts) { workout in
NavigationLink(value: workout) {
Text(workout.shortName)
}
}
.navigationDestination(for: Workout.self) { workout in
WorkoutSetupView()
}
.navigationDestination(isPresented: $workoutManager.showingSummaryView) {
SummaryView()
}
}
.environmentObject(workoutManager)
}
}
struct Workout: Identifiable, Hashable {
var id: String
var shortName: String
static var workouts: [Workout] {
[
Workout(id: "WORKOUT1", shortName: "Workout 1"),
Workout(id: "WORKOUT2", shortName: "Workout 2")
]
}
}
class WorkoutManager: NSObject, ObservableObject {
#Published var showingSummaryView: Bool = false
func endWorkout() {
showingSummaryView = true
}
}
struct SummaryView: View {
#Environment(\.dismiss) var dismiss
var body: some View {
ScrollView {
VStack{
Text("Results: 2")
Button("Done") {
dismiss()
}
}
}
.navigationTitle("Summary")
.navigationBarBackButtonHidden(true)
}
}
struct WorkoutSetupView: View {
var body: some View {
NavigationLink(
destination: SessionPagingView()) {
Image(systemName: "play")
}
}
}
struct SessionPagingView: View {
#EnvironmentObject var workoutManager: WorkoutManager
#State private var selection: Tab = .session
enum Tab {
case controls, session
}
var body: some View {
TabView(selection: $selection) {
Button {
workoutManager.endWorkout()
} label: {
Text("End")
}.tag(Tab.controls)
Text("0:10").tag(Tab.session)
}
.navigationBarBackButtonHidden(true)
}
}
If I replace the NavigationStack block in ContentView with the code below, the app works in a simplified way as expected but a new workout cannot be initiated without relaunching the app.
NavigationStack {
NavigationLink(
destination: SessionPagingView()) {
Image(systemName: "play")
}
.navigationDestination(isPresented: $workoutManager.showingSummaryView) {
SummaryView()
}
}
I've tried resetting showingSummaryView to false on pressing Done in SummaryView and also tried using a NavigationPath but neither had any noticeable effect.
Thanks

SwiftUI tap picker like a button

Is it possible to make a SwiftUI picker tap like a button. The following only lets you set the picker if you tap on the text, but I would like to tap the picker like a button.
struct ContentView: View {
#State var myvar: String = ""
var body: some View {
Picker(selection: $myvar, label:
Text("Phone Type")) {
Text("Home").tag(0)
Text("Service").tag(1)
Text("Work").tag(2)
Text("Cell").tag(3)
Text("Other").tag(4)
}.padding().border(Color.gray)
}
}
Thanks for any help!
you could try this with .pickerStyle. Note that your tag(...) needs to match the type in myvar, so using string, like this:
struct ContentView: View {
#State var myvar: String = ""
var body: some View {
Picker(selection: $myvar, label: Text("Phone Type")) {
Text("Home").tag("Home")
Text("Service").tag("Service")
Text("Work").tag("Work")
Text("Cell").tag("Cell")
Text("Other").tag("Other")
}
.pickerStyle(.segmented)
.padding().border(Color.gray)
}
}
#Asperi was right in their comment. The Menu is better than the picker when wanting it to display as a button. Here is a simple code example.
struct ContentView: View {
#State var myvar: String = ""
var body: some View {
Menu {
// This should be done in a ForEach loop
Button("Home", action: {myvar="Home"})
Button("Service", action: {myvar="Service"})
Button("Work", action: {myvar="Work"})
Button("Cell", action: {myvar="Cell"})
Button("Other", action: {myvar="Other"})
} label: {
//THIS ALLOWS CLICKING TO OPEN ON THE BLUE BACKGROUND
Text("ClickHere")
.frame(width: 200, height: 100, alignment: .center)
.background(Color.blue)
.foregroundColor(Color.white)
}
}
}

Menu on NavigationLink is not tapable

How can I put a Menu on a NavigationLink destination? This approach opens only the destination and never the menu when I tap on it.
var body: some View {
NavigationView {
NavigationLink (
destination: Text("Hello")
label: {
ZStack(alignment: .topTrailing){
Rectangle()
.frame(width: 300, height: 100)
Menu {
Button("Order Now", action: {print("1")})
Button("Adjust Order", action: {print("2")})
} label: {
Label("Options", systemImage: "paperplane")
}
}
})
.buttonStyle(PlainButtonStyle())
}
}
EDIT: Just to clarify: The Menu is in the top right corner. When I tap on this menu button, the menu should appear. Otherwise when I tap on the rest of the view the destination of the NavigationLink should be opened
Okay.. third time's a charm.. so they say!
I made it harder than what it needed to be but that is a working example of Menu and I learned from this myself
import SwiftUI
struct navViewTest: View {
var body: some View {
NavigationView {
VStack {
Text("Hello World")
}.navigationBarItems(trailing:
Menu {
Button("Option 1", action: {doSomething() })
Button("Option 2", action: {doSomething() })
} label: {Text("Menu") }
)
}
}
private func doSomething() { }
}
struct navViewTest_Previews: PreviewProvider {
static var previews: some View {
navViewTest()
}
}
I've found a "solution". A little bit hacky, but it works so far. I just wrapped the menu in a button:
Button(action: {/*do something*/}, label: {
Menu {
Button("Option 1", action: {print("1") })
Button("Option 2", action: {print("2") })
} label: {Text("Menu") }
})

Make a .contextMenu update with changes from PasteBoard

I have a contextMenu view modifier for a view like this one:
Text("Some Text")
.contextMenu {
Button(action: {
editCodes(withTappedCode: codeOnDisplay, delete: true)
}, label: {
Text("Paste")
Image(systemName: "doc.on.clipboard")
})
.disabled(!UIPasteboard.general.contains(pasteboardTypes: [aPAsteBoardType]))
}
The button should only be enabled when a certain Pasteboard Type is available. However this doesn't happen.
The disabled state is set when the context menu for the Button is first shown. After this any changes to the pasteboard will not modify the disabled state, even if the menu is closed and opened again.
This seems to only to happen if the modified view is refreshed in any way.
How can I change the disabled state, for the context menu button, with the Pasteboard type?
You can listen to UIPasteboard.changedNotification to detect changes and refresh the view:
struct ContentView: View {
#State private var pasteDisabled = false
var body: some View {
Text("Some Text")
.contextMenu {
Button(action: {}) {
Text("Paste")
Image(systemName: "doc.on.clipboard")
}
.disabled(pasteDisabled)
}
.onReceive(NotificationCenter.default.publisher(for: UIPasteboard.changedNotification)) { _ in
pasteDisabled = !UIPasteboard.general.contains(pasteboardTypes: [aPAsteBoardType])
}
}
}
(You may also want to use UIPasteboard.removedNotification).

Navigating to a new screen using a button in swiftUI

I am new to swiftUI and am having difficulty moving from one UIViewController to another UIViewController. Rights now I have a state called navigate and a button, but I am not sure how to move this screen to a new screen. Here is the code.
import SwiftUI
struct ContentView: View {
#State var navigate = false
var body: some View {
Button(action: { self.navigate.toggle() }) {
Text("Get Involved")
.font(.custom("PlayfairDisplay-Regular", size: 18))
.foregroundColor(Color("Dark Text"))
}
}
}
Here is the code for the blank screen.
import SwiftUI
struct GoalIdeasView: View {
var body: some View {
Text("Hello")
}
}
How would I go about navigating to the second screen from the ContentView Controller once the button is pressed?
There is a view called Navigation that act like UINavigationViewController. Then you can use a NavigationLink to navigate between views. So it would be like:
struct ContentView: View {
var body: some View {
NavigationView {
NavigationLink("Get Involved", destination: GoalIdeasView())
.font(.custom("PlayfairDisplay-Regular", size: 18))
}
}
}