Tap on SwiftUI LocationButton in UI Test - swift

I am unable to tap on the LocationButton in a Swift UI Test. This is the button:
I am in a breakpoint in a UITest and I tried this without luck:
app.buttons.count returns 0
app.staticTexts["Current Location"].exists returns false
app.buttons["Current Location"].exists returns false
Is there something special about this button? How can I tap it in a UITest?

VStack {
LocationButton(.shareCurrentLocation) {
locationManager.requestLocation()
}
.cornerRadius(30)
.symbolVariant(.fill)
.foregroundColor(.white)
}
.accessibilityIdentifier("buttonStack")
let button = app.otherElements["buttonStack"]
Found workaround with this way but not pretty.

Related

How do you change a Text field .onTapGesture (Swift5, Xcode12.4)?

Seemingly simple but I can't seem to find any results on this.
I have this code
struct ContentView: View {
var MinimalTimer: MinimalistTimer = MinimalistTimer()
var body: some View {
Text("\(MinimalTimer.fourteenMinutes)")
.onTapGesture {
MinimalTimer.decrementTimer()
}
}
}
and I want to tap the timer and display on the text the new time, e.g. 840 -> 839 -> 838.
How do you change the display text on tap?
Timer example
I dont know whether this will work in swiftUI though but try , MinimalTimer.addGestureRecognizer(<#T##gestureRecognizer: UIGestureRecognizer##UIGestureRecognizer#>) there is method something like this , you'll get it with autoComplete
Look for gesture Recogniser

Toggle Sidebar in Code using SwiftUI NavigationView on iPad

I'm trying to utilize the built-in sidebar from SwiftUI 2.0 by using NavigationView like this:
NavigationView {
MainView()
ListView()
DetailView()
}.navigationBarHidden(true)
But since I want to use my own Custom Back Button, I've hidden the NavigationBar and tried to toggle the sidebar with code which doesn't work.
self.presentationMode.wrappedValue.dismiss()
I've already seen a lot of solutions for macOS:
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
But I can't seem to find equivalent for iPad, thanks in advance.
I used this code to change the default sidebar settings:
extension UISplitViewController {
open override func viewDidLoad() {
super.viewDidLoad()
self.preferredDisplayMode = .secondaryOnly
self.preferredSplitBehavior = .overlay
}
}
self exposes several sidebar methods and properties that can be used. I hope it will be useful!
So this is not a good long term solution but if you are like me and 100% needed the native approach to work here's how it can be hacked. Using https://github.com/siteline/SwiftUI-Introspect you can find the right view controller in the hierarchy and set the display mode.
Text("Some View").introspectViewController { vc in
guard let splitVC = vc.parent?.parent as? UISplitViewController else {
return
}
splitVC.preferredDisplayMode = .oneBesideSecondary
}
This is BRITTLE but it works.

SwiftUI TapGesture onStart / TouchDown

I am using the TapGesture in SwiftUI for MacOS. TapGesture is only recognized on TouchInsideOut event, when releasing the press again. I want to call an action on touchdown and another on the end gesture.
There is a onEnded state available for TapGesture but no onStart.
MyView()
.onTapGesture {
//Action here only called after tap gesture is released
NSLog("Test")
}
Is there any chance to detect touch down and touch release?
I tried using LongPressGesture aswell, but couldn't figure it out.
If by pure SwiftUI then only indirectly for now.
Here is an approach. Tested with Xcode 11.4.
Note: minimumDistance: 0.0 below is important !!
MyView()
.gesture(DragGesture(minimumDistance: 0.0, coordinateSpace: .global)
.onChanged { _ in
print(">> touch down") // additional conditions might be here
}
.onEnded { _ in
print("<< touch up")
}
)
The selected answer generates a continuous stream of events on touch, not just one on touch-down.
This works as requested:
MyView()
.onLongPressGesture(minimumDuration: 0)
{
print("Touch Down")
}

How to get accentColor background for menu items in SwiftUI with reduced transparency?

Using AppKit you can add NSMenuItems to an NSMenu. I saw that there is something similar to NSMenu in SwiftUI, namely MenuButton. But I cannot find any documentation on how it works.
I tried the following:
MenuButton("+") {
Button("New contact") { print("Create new contact") }
Button("New group") { print("Create new group") }
}
And that gives me this
It looks almost OK but when I enable the "Reduce transparency" in system preferences
The buttons have a different background color than the menu (notice the slightly lighter color above and beneath the menu items).
When I hover the menu items, their background color doesn't change like a normal macOS menu. See the image below:
I also tried to change the background color manually using the .background() modifier but that doesn't affect the full width of the menu item.
MenuButton("+") {
Button("New contact") { print("Create new contact") }
.background(Color.accentColor)
Button("New group") { print("Create new group") }
}
I suppose this is because I am placing Buttons inside the MenuButton while it is probably expecting some other SwiftUI element. What elements should I place inside MenuButtons to create a normal looking macOS menu like the one below?
[Update] macOS Big Sur
I also tried this out in Big Sur. While the background renders correctly, in Big Sur, the text color is messed up now. 🤯
I think I found a partial solution to this by configuring a ButtonStyle and applying it to the MenuButton generic structure. Note however that the conditional change of .foregroundColor isn't inherited by the individual's Button()'s Text(). Also the color ain't right.
Perhaps someone wants to improve on this.
struct DetectHover: ButtonStyle {
#State private var hovering: Bool = false
public func makeBody(configuration: DetectHover.Configuration) -> some View {
configuration.label
.foregroundColor(self.hovering ? Color.white : Color.primary)
.background(self.hovering ? Color.blue : Color.clear)
.onHover { hover in
self.hovering = hover
}
}
}
MenuButton(label: Image(nsImage: NSImage(named: NSImage.actionTemplateName)!)) {
// Buttons
}.buttonStyle(DetectHover())
I’ve got something that nearly looks right. I have created a custom button which does change appearance when hovered.
I am very new to both Swift and SwiftUI, so the following may be clumsy. I would welcome any improvements.
struct HoverButton: View {
var text = "Hover Button"
var action = {}
#State private var hovering = false
var body: some View {
Button(text, action: action )
.padding(.horizontal, 6)
.padding(.vertical, 2)
.buttonStyle(PlainButtonStyle())
.frame(minWidth: 0, maxWidth: .infinity)
.background(self.hovering ? Color(.selectedMenuItemColor) : Color(.clear))
.onHover { hover in
self.hovering = hover
}
}
}
// Usage:
MenuButton("Test") {
HoverButton(text: "Apple", action: {
print("Apple")
})
HoverButton(text: "Banana", action: {
print("Banana")
})
}
.font(.system(size: 14, /* weight: .heavy, */ design: .default))
.menuButtonStyle(BorderlessButtonMenuButtonStyle())
The whole point in creating the custom button is to have access to Self so that I can change its appearance. However, there are some serious shortcomings:
The main problem is that the buttons don’t take the full width of the menu body. I have no idea how to fix that.
The menu buttons are centered. I tried using a HStack with a Spacer() but that didn’t help.
I have no idea how to omit the first parameter name as for a real Button

Swiftui macos image as a button rendering as an image on top of a small button

From looking at code online, it seems the following code in swiftUI
Button(action: {
print("Button tapped!")
}) {
Image("iFEN")
}
should render the image iFEN as a button that can be clicked. However - instead it renders this:
, the image on top of a small button that can be clicked. Why is this the case? Is something different between macos and ios in this case?
You need to use a different button style:
Button(action: {
print("Button tapped!")
}) {
Image("iFEN")
}
.buttonStyle(PlainButtonStyle())
Button styles can vary drastically by platform. Here is a table that shows which styles are available on each.