Access Button Tap In Unit test SwiftUI - swiftui

I'm trying to access the button click in the Unit test class. I'm not able to perform action.
Button AppCode:-
var body: some View {
VStack(spacing: 10) {
Button(action: {
print("Button Cicked")
}) {
Text("Testing")
.frame(width: 50, height: 20)
.padding()
.foregroundColor(.white)
.cornerRadius(40)
.accessibility(label: Text("Testing"))
}
Spacer()
}
}
Unit Class Code:-
class testBtnClickked: XCTestCase {
func testtestBtnClickked() {
let app = XCUIApplication()
let deleteButton = app.buttons["Testing"]
deleteButton.click()
}
}
Error:- No target application path specified via test configuration: <XCTestConfiguration: 0x7fb315a11a80>
Can someone please explain to me how to access the button click in the XCTest Class, I've tried to with above code but no results yet.
Any help would be greatly appreciated.
Thanks in advance.

This issue occurs, because i'm not using the UI test class. I'm using UI test code in the Unit Class.
After Create the Different UI test Class, the same code working perfectly.

Related

SwiftUI: present popover on iPhone?

I am looking at the Apple Reminders app and want to build the same pop over like view on iPhone. This the screen I am referring to:
So, I can present a popover like UI on an iPad using the popover modifier.
struct ContentView: View {
// 1.
#State var isPopoverPresented = false
var body: some View {
// 2.
Button(action: {
self.isPopoverPresented = true
}) {
Text("Some content")
}
// 3.
.popover(isPresented: $isPopoverPresented) {
Text("Popover is Presented")
.font(.largeTitle)
.frame(width: 200, height: 300)
}
}
}
However when that code runs on an iPhone, the popover turns in a fullscreen modal coming up from the bottom.
I want to know if there is a native way to build a screen like the one shown in the Reminders app or, is that screen a custom View with custom layout logic on an iPhone?
You're looking for a Menu (UIMenu in UIKit). Note that it's iOS 14+ only.

SwiftUI Mac: Adding buttonStyle Makes Button Click Propagate to Parent

I am trying to create a situation in a SwiftUI Mac app where when I click a Button inside a parent view, only the Button's action is trigger—not any of the tap gestures attached to its parent.
Here is a simple example that reproduces the problem:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 30){
Button("Button"){
print("button clicked")
}
.padding(5)
.background(Color.blue)
.buttonStyle(PlainButtonStyle())
}
.frame(width: 500, height: 500)
.background(Color.gray)
.padding(100)
.gesture(TapGesture(count: 2).onEnded {
//Double click (open message in popup)
print("double click")
})
.simultaneousGesture(TapGesture().onEnded {
if NSEvent.modifierFlags.contains(.command){
print("command click")
}else{
print("single click")
}
})
}
}
Clicking the button triggers both button clicked and single click.
If you comment out the buttonStyle...
//.buttonStyle(PlainButtonStyle())
It works how I want it to. Only button clicked is fired.
It doesn't seem to matter which button style I use, the behavior persists. I really need a custom button style on the child button in my situation, so how do I get around this?
If you replace your .simultaneousGesture with a regular .gesture it works for me – and still recognizes the outer single and double taps.

SwiftUI: use Link with onTapGesture

I want to specify a Link in my SwiftUI view, but I also want to register when/if that Link was actually tapped or not. What's the best way to do this? I tried adding an onTapGesture on the Link (which is a View I believe):
Link("Test", destination: URL(string: "testThreePanel://")!)
.onTapGesture {
print("Testing button link tapped")
}
But the onTapGesture doesn't get invoked on tapping the link.
Is there another way to do this?
you could try this, works for me, but only on iOS15 / macOS12:
// iOS15 / macOS12 only
Link("Test", destination: URL(string: "https://duckduckgo.com")!)
.environment(\.openURL, OpenURLAction { url in
print("---> testing link actioned")
return .systemAction
})
Just enable firing both embedded and added gestures simultaneously.
struct ContentView: View {
var body: some View {
Link("Test", destination: URL(string: "testThreePanel://")!)
.simultaneousGesture(
TapGesture()
.onEnded { val in
print("Testing button link tapped \(val)")
}
)
}
}
For iOS 15 it also works if you remove the 'url' parameter, just in case you don't want to use it.
Link("Test", destination: URL(string: "https://duckduckgo.com")!)
.environment(\.openURL, OpenURLAction { _ in
print("---> testing link actioned")
return .systemAction
})

Navigation View Formatting Trouble

I'm a newbie, using XCode 13.0 to create a very basic app that needs to have a Settings view. I'd like to navigate to the Settings view on tapping a label. To do that, it seemed sensible to use a NavigationView with a NavigationLink.
Unfortunately, I'm encountering a formatting issue that creates a mess of the HStack in which the Setting label (gear icon) resides, as show below:
This is what I want, a result of the following code:
HStack(spacing: 25) {
... other labels
Label ("", systemImage: "gear")
.foregroundColor(.gray)
.font(.title)
.onTapGesture(perform: {
// Set a state variable that triggers an extension
// that brings up the SettingsView
})
}
This is what happens when NavigationView encapsulates the gear icon label. Note the vertical and horizontal white space around it.
HStack(spacing: 25) {
... other labels
NavigationView {
NavigationLink(destination: SettingsView()) {
Label ("", systemImage: "gear")
.foregroundColor(.gray)
.font(.title)
}.navigationBarTitle(Text(""))
}
}
I've, literally, spent weeks (sporadically) on this issue, looking up dozens of answers and trying various formatting options, without luck. I've also tried encapsulating parent and grandparent stacks into the NavigationView. To no avail. Surely, this is something trivial. Can somebody point me in the right direction?
p.s. there are other issues in that that Navigation link opens as a sub-window; I plan to tackle that later.
Edit: Right, so I tried using Yrb's code:
HStack(spacing: 25) {
... other labels
NavigationView {
NavigationLink(destination: Text("Linked View")) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
}
.fixedSize()
.background(Color.red)
}
]
Unfortunately, there's no substantive change...
In diagnosing these sort of issues, it helps to throw a .background() with a color on. You can then see the issue. In this case, it was twofold, one, you need to use a .fixedSize to shrink the view to its smallest dimensions necessary. That would leave you with the icon plus a little space. That was due to you using a label as it was leaving a spot for the Text("") that you used as a fill in. Since you only want the image, use Image(systemName:) The code then comes out like this:
struct NavLinkNoSpace: View {
var body: some View {
NavigationView {
NavigationLink(destination: Text("Linked View")) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
}
.fixedSize()
// Setting this shows you what space you are using. Remove it when you are done
.background(Color.red)
}
}
}
A couple more things. If you have not ever set the NavigationTitle, you don't need to set it to "". In your example, there was no title, so I simply removed it and there was no effect.
More importantly, and it was addressed by some of the comments, you should only have one NavigationView in the view hierarchy. As long as you are in the hierarchy, you do not need to wrap things like NavigationLink to have them work. You can always throw one around your view call in the preview provider if you are in a child view, to show what things look like, and to test NavigationLinks, etc., but do not just put them in to your main code. It will lead to undesirable outcomes.
To summarize what worked to fix the primary problem, that of formatting: The key was in figuring what to encapsulate within the NavigationView. My mistake was to assume that only the NavigationLink needed to be in the NavigationView.
What worked was to place all the contents of the body into the NavigationView, like below:
var body: some View {
NavigationView {
VStack(spacing: -10) {
Text(appName)
.font(.largeTitle)
.foregroundColor(.blue)
.padding(.bottom)
// ...
// includes a bunch of VStacks and HStacks
// ... and finally
NavigationLink(destination: SettingsView()) {
Image(systemName: "gear")
.foregroundColor(.gray)
.font(.title)
// ... more stuff
// ... and finally
}.padding(.top, -100) // NavigationView
} // body

Is it possible to make a modal non-dismissible in SwiftUI?

I am creating an App where the login / register part is inside a modal, which is shown if the user is not logged in.
The problem is, that the user can dismiss the modal by swiping it down...
Is it possible to prevent this?
var body: some View {
TabView(selection: $selection) {
App()
}.sheet(isPresented: self.$showSheet) { // This needs to be non-dismissible
LoginRegister()
}
}
Second example:
I am using a modal to ask for information. The user should not be able to quit this process except by dismissing the modal with save button. The user has to input information before the button works. Unfortunately the modal can be dismissed by swiping it down.
Is it possible to prevent this?
iOS 15 and later:
Use .interactiveDismissDisabled(true) on the sheet, that's all.
Prev iOS 15:
You can try to do this by using a highPriorityGesture. Of course the blue Rectangle is only for demonstration but you would have to use a view which is covering the whole screen.
struct ModalViewNoClose : View {
#Environment(\.presentationMode) var presentationMode
let gesture = DragGesture()
var body: some View {
Rectangle()
.fill(Color.blue)
.frame(width: 300, height: 600)
.highPriorityGesture(gesture)
.overlay(
VStack{
Button("Close") {
self.presentationMode.value.dismiss()
}.accentColor(.white)
Text("Modal")
.highPriorityGesture(gesture)
TextField("as", text: .constant("sdf"))
.highPriorityGesture(gesture)
} .highPriorityGesture(gesture)
)
.border(Color.green)
}
}
This is a common problem and a "code smell"... well not really code but a "design pattern smell" anyway.
The problem is that you are making your login process part of the rest of the app.
Instead of presenting the LoginRegister over the App you should really be showing either App or LoginRegister.
i.e. you should have some state object like userLoggedIn: Bool or something and depending on that value you should show either App or LoginRegister.
Just don't have both in the view hierarchy at the same time. That way your user won't be able to dismiss the view.
If you dont mind using Introspect:
import Introspect
#available(iOS 13, *)
extension View {
/// A Boolean value indicating whether the view controller enforces a modal behavior.
///
/// The default value of this property is `false`. When you set it to `true`, UIKit ignores events
/// outside the view controller's bounds and prevents the interactive dismissal of the
/// view controller while it is onscreen.
public func isModalInPresentation(_ value: Bool) -> some View {
introspectViewController {
$0.isModalInPresentation = value
}
}
}
Usage:
.sheet {
VStack {
...
}.isModalInPresentation(true)
}
iOS 15+
Starting from iOS 15 you can use interactiveDismissDisabled.
You just need to attach it to the sheet:
var body: some View {
TabView(selection: $selection) {
App()
}.sheet(isPresented: self.$showSheet) {
LoginRegister()
.interactiveDismissDisabled(true)
}
}
Regarding your second example, you can pass a variable to control when the sheet is disabled:
.interactiveDismissDisabled(!isAllInformationProvided)
You can find more information in the documentation.
theoretically this may help you (I didn't tryed it)
private var isDisplayedBind: Binding<Bool>{ Binding(get: { true }, set: { _ = $0 }) }
and usage:
content
.sheet(isPresented: isDisplayedBind) { some sheet }