Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tappable SwiftUI view that is not tapped by KIF #1271

Open
babbage opened this issue Dec 11, 2022 · 5 comments
Open

Tappable SwiftUI view that is not tapped by KIF #1271

babbage opened this issue Dec 11, 2022 · 5 comments

Comments

@babbage
Copy link

babbage commented Dec 11, 2022

I have the following SwiftUI view that inserts a tappable row in a List:

struct AddItemRow: View {
    let prompt: String
    let action: (() -> Void)?
    
    var body: some View {
        HStack {
            ZStack {
                Image(systemName: "circle.fill")
                    .foregroundColor(.white)
                    .font(.system(.title2))
                    .padding(.leading, 0)
                Image(systemName: "plus.circle.fill")
                    .foregroundColor(.green)
                    .font(.system(.title2))
                    .padding(.leading, 0)
            }
            .accessibilityHidden(true)
            
            Text(prompt)
                .padding(.leading, 8.0)
                .accessibilityRemoveTraits(.isStaticText)
                .accessibilityIdentifier(prompt)
        }
        .frame(minHeight: 32.0)
        .onTapGesture(count: 1, perform: {
            action?()
        })
        .accessibilityAddTraits(.isButton)
        .accessibilityElement(children: .combine)
    }
}

I have KIF tests that previously worked with this view, before SwiftUI was adopted for this list, the main content of the screen, as a subview in a UIHostingController.

My test calls the following support function:

func addRow(_ type: ContactComponent) {
    let identifier = "add " + type.rawValue
    tester().waitForAnimationsToFinish()
    tester().tapView(withAccessibilityIdentifier: identifier) // brings row into view
    tester().waitForAnimationsToFinish()
    tester().tapView(withAccessibilityIdentifier: identifier)
    tester().waitForAnimationsToFinish()
}

Previously this worked correctly. Now, the row in question scrolls into view, but the second tap does not activate the row.

Things I have tried:

  1. The row works fine in actual use.
  2. In Accessibility Inspector, the row is selectable, the label is the expected correct label, it has a button trait, and clicking "Activate" correctly activates the row.
  3. Altering the view so this row is already on screen when the view is first displayed does not make any difference. The row does not register a tap.
  4. Manually clicking the row in the iOS Simulator while the test is running correctly activates the action, which adds a row to the list, and the KIF test then successfully goes on to interact correctly with the textfields in that additional row.
  5. I have tried multiple ways to restructure the addItem view, such as setting .accessibilityElement(children: hidden) and then applying the accessibiltyLabel and accessibilityIdentifier to the whole view, or moving the order of the modifiers so the onTapGesture appears before or after the other view modifiers. This does not change anything. I am willing to try other combinations that might be suggested but it is likely I've already tried them. 😕

Any leads would be appreciated. I am about to undertake a complete redesign of the rest of the app in SwiftUI. This is currently the only section in SwiftUI and the only place my KIF tests are failing. I would like to migrate forward my KIF tests if I can but I clearly need KIF to be able to tap SwiftUI elements that are user-tappable.

@babbage
Copy link
Author

babbage commented Dec 11, 2022

Digging into other issues that might be related, found #1250

From that, found that - (BOOL)hasTapGestureRecognizerAndIsControlEnabled returns NO for this element, which I thought was perhaps why it was not being tapped. That code appears to assume that relevant gestures will be of type UITapGestureRecognizer, whereas the type of my gesture appears to be SwiftUI.UIKitGestureRecognizer which is a subclass of UIGestureRecognizer but not UITapGestureRecognizer.

Output of hasTapGestureRecognizerAndIsControlEnabled indicating NO for this element

The function thus returns NO and the view is not tapped.

However, editing this function to always return YES did not resolve my issue... so clearly there is more to the problem than just the output of this function.

@justinseanmartin
Copy link
Contributor

Thanks for the context and digging in. I'd propose adding || [obj isKindOfClass:NSClassFromString(@"SwiftUI.UIKitGestureRecognizer")] or something like that to hasTapGestureRecognizerAndIsControlEnabled if that fixes your case. It is unfortunate that the SwiftUI gesture recognizer doesn't inherit from the more specific type, but there are presumably reasons for that. I don't see anything obvious exposed from UIKitGestureRecognizer that could be used to determine that it is specifically waiting for tap events.

It could also potentially be the right thing to do to loosen the requirement and generally let any gesture recognizer pass that check. I'm not sure of the unexpected elements that might get picked up from being more permissive though. For that reason, I might err on the side of caution and only add the specific gesture recognizer SwiftUI.UIKitGestureRecognizer known to be causing problems today, with a comment referring to potentially making it more general.

@mjoe23
Copy link

mjoe23 commented Jan 18, 2024

Is there any update or fix for this?

@marinofelipe
Copy link
Contributor

Hey there, did anyone had a chance to look into it? I'd be willing to contribute in case no one is looking into it :)

@justinseanmartin
Copy link
Contributor

I haven't heard of anyone starting to pick this up. We may have reason to look into SwiftUI support in the near future on my end, but it isn't a pressing need at the moment. If this is actively problematic for you, it'd be great if you wanted to start digging into this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants