I have my code in Swift 3. I have two UITextFields and a UIButton in my view. I have the textFieldShouldEndEditing delegate of the first UITextField which is fired when I focus on the 2nd UITextField. However when I tap on the UIButton this delegate is not fired. I had thought that when a UIButton gets focus then the UITextFieldâs event will automatically fire since it is losing focus. However, when I tap on the UIButton, the cursor is still blinking in the UITextField, which means it is not losing focus.
Any ideas on why the UIButton is not getting focus will be most appreciated.
Thanks.
This is an infuriating aspect of developing forms on iOS. Button taps don't remove focus from UITextField / UITextView the same way they would in an HTML form.
I'm sure there are more elegant ways to solve this, but if I want to consistently do this across all forms I ensure that the following line of code is run when users tap on any buttons of mine.
- (void)userDidTapButton:(id)sender
{
[[[UITextField new] becomeFirstResponder] resignFirstResponder]
// the above embarrassing line of code basically creates a new textfield
// puts focus in it (thereby removing focus from any existing textfields
// and then removes focus again
// this ensures that delegate events fire on your existing textfields
// finally, run any other button code
}
Related
TL;DR: Is there any way to have custom button style (custom pressed state) in SwiftUI on tvOS while it is still working correctly with Accessibility Focus API and therefore its hasFocus works in UI tests?
Please visit dedicated Github repo for a longer version and a sample project with UI tests example.
What is the problem?
To create a custom button in SwiftUI on tvOS and can customize it based on pressed state, you can implement custom ButtonStyle.
Button(...)
.buttonStyle(AnyCustomButtonStyle())
and then use ButtonConfiguration.isPressed in the view.
However, I have found that although the button visually looks focused, it does not really report as Focused in Accessibility API. See the sample project for example and actual test showing the problem.
Among other problems, it makes the button quite difficult to work with in tvOS UI tests. tvOS is relying on focus for navigation and because the button's hasFocus always stays false (even if the button renders in focused appearance), it can prevent a lot of useful APIs to work in tests.
Why do you need custom ButtonStyle on tvOS?
I know Apple provides some custom PrimitiveButtonStyle implementations (like CardButtonStyle), but those don't provide enough flexibility. They all modify your button (e.g. add background).
Not being able to use custom buttonStyle makes it impossible to implement for example Capsule-style buttons like this...
First button focused
First button focused and pressed
Please let's leave aside the discussion if it is a good idea or not đ ... Just trying to find if there is a solution or if it is eventually a bug of SwiftUI.
What is the issue with Accessibility?
Without custom button, the button reports as Focused to Accessibility in UI tests.
Without custom style
(lldb) po app
Attributes: Application, 0x12ed10b30, pid: 61273, label: 'FocusSwiftUI'
Element subtree:
âApplication, 0x12ed10b30, pid: 61273, label: 'FocusSwiftUI'
...
Button, 0x12ed0c660, {{468.0, 477.0}, {298.0, 126.0}}, label: 'Button 1'
Button, 0x12ed0c770, {{453.0, 470.7}, {328.0, 138.7}}, Focused
Button, 0x12ed0c1e0, {{806.0, 477.0}, {303.0, 126.0}}, label: 'Button 2'
Button, 0x12ed0c2f0, {{791.0, 470.8}, {333.0, 138.5}}
...
However, the moment you set buttonStyle to any custom one, the output changes drastically...
(lldb) po app
Attributes: Application, 0x106d0cd40, pid: 70156, label: 'FocusSwiftUI'
Element subtree:
âApplication, 0x106d0cd40, pid: 70156, label: 'FocusSwiftUI'
...
Button, 0x106d0ce50, {{707.0, 505.0}, {142.0, 71.0}}, label: 'Button 1'
Button, 0x106d0cf60, {{885.0, 505.0}, {146.0, 71.0}}, label: 'Button 2'
Button, 0x106d0d180, {{1067.0, 505.0}, {147.0, 71.0}}, label: 'Button 3'
...
Notice there is no more Focused button anywhere... We will never get any button with hasFocus == true in queries for example...
What else did I try?
I tried to experiment with many (probably all) .accessibility... modifiers before asking.
Many different results, but none of them ever had proper focus behavior in UI tests...
.accessibilityChildren: close to default button behavior in terms of accessibility structure (e.g. nested buttons)
.accessibilityRepresentation: button never gets visually highlighted
Temporary workaround
For now there seems to be no solution in sight. I have implemented rather complicated hack to get at least UI tests working for now.
In a nutshell: When the app is running in a context of UI tests (determined through Launch Arg), each affected button adds clear color background (it has no visual or behavioral impact) when focused. The background color then has a constant accessibilityIdentifier (e.g. "MY_FOCUSED", I call it "custom focus marker"). When evaluating if element is focused in UI tests, I then check if the button contains child element where accessibilityIdentifier == "MY_FOCUSED".
It is nasty, but somehow good enough for UI tests and actually works reliably so far. It works thanks to the fact there is always only one focused item at the same time and the "if focused -> set background" takes care of the automatic update of the "custom focus marker".
You can try to set accessibilityRepresentstion - does it help?
Button(..)
.buttonStyle(AnyCustomButtonStyle())
.accessibilityRepresentation {
Button(..)
}
Edit:
Another idea - what about having the âcorrectâ Button in the background of your custom? Something like:
Button(..)
.buttonStyle(AnyCustomButtonStyle())
.background(
Button(..).opacity(0.0001) //it may work even with opacity 0
)
You may additionally improve it with making the visible button hidden to accessibility with .accessibility(hidden: true)
If you're looking for actual VoiceOver accessibility (not using it as a UI test tool I mean) you can use the isFocused environment variable and on change, call UIAccessibility.post(notification: .announcement, argument: "Your message")
Actually i'm developing a Apple Tv project in swift3.I faced some problem with focus navigation using remote.
Is there any way to change focus from one object to another object which i want.
I tried below code but not working......
override var preferredFocusedView: UIView? {
get {
return preferredFocusedView1
}
}
In the bellow screenshot i want to move focus from the pink icon to the star button when pressed down button of remote. how it is possible. plz provide me some code in details
I have a problem and I can't find the solution for it. At the moment I have a tap gesture recogniser in my VC's viewDidLoad() that will dismiss the keyboard if a tap is recorder on screen. Problem is that when the user has finished completing the text fields and he presses the "Register Account" button it is still recognised as a tap and only removes the keyboard but the button does not respond to the tap and the selector function is not called.
I want as soon as the button was tapped with the keyboard on screen to call the function and do its business. Please help.
Just posting the first line of the function called by the button because for this question is as far as I have got:
#objc fileprivate func createAccount() {
confirmPasswordTextField.resignFirstResponder()
If you just want to make all buttons actionable (along with the keyboard dismissing) all you have to do is make your tap gesture recognizer not cancel touches in view:
var cancelsTouchesInView: Bool { get set }
So just set:
myTapGesture.cancelsTouchesInView = false
https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624218-cancelstouchesinview
This will make it so that when the user taps on the "Register Account" button the keyboard will dismiss and the action will also be performed.
If I click anywhere on the dialog window, like in the "background", or on a Static Text, the function OnLButtonDown() is fired. But if I click a ListBox/EditBox/Combo/Calendar/Checkbox etc is not fired.
I thought that because these have control variables atached to it, and the Static Text don't. But adding a new Listbox to my Dialog and testing, I see that it doesn't fire either, so now I am confused...
I added OnLButtonDown() with the Class Wizard, it appears in:
BEGIN_MESSAGE_MAP(CMFCTesting2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
// other handlers, etc
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
My function:
void CMFCTesting2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{
AfxMessageBox(CString("BUTTON DOWN"));
CDialogEx::OnLButtonDown(nFlags, point);
}
I tried calling CDialogEx:: ... before AfxMessageBox... but same result.
The CListBox item, has the Notify option set to True, as I seen some advices in other posts.
I suspect that the WM notification message is captured somehow by the ListBox, and not sent "forward" to my CMFCTesting2Dlg, but I understood that this should happen with the Notify option set to True, on the ListBox, no? (well, obviously, not...)
Sorry, I am new to dealing with WM in MFC.
BTW, I use Visual Studio 2010 Ultimate, and this is a Visual C++ - MFC- MFC Application - Dialog Based project.
How can I capture this mouse down event if clicked on a listbox / combo / etc?
On the LONG STORY, I am actually trying to accomplish this issue:
I have two listboxes (lets say), and I want to scroll them synchronously, when user scrolls one of them, others must scroll the same (or update in the next moment). And I thought of using on mouse down to track the position of every Listbox (with the GetTopIndex), and on mouse up, to GetTopIndex again and compare with the previous ones. If a change was made, then a listbox was scrolled and then update all listboxes with SetTopIndex. The unfriendly solution for the user, but simpler for me, would be clicking a button that does this verification / update, but its not elegant at all, and it can only set them at the same level, but can't determine which one was scrolled last time. This automatically scrolling of the listboxes should be made just for displaying, it is not important the selections in the listboxes. I tried using the Message Type in the Add Event Handler for the Listbox, but none that are displayed work for my problem, KillFocus and SetFocus, are not fired if the scroll-bar is dragged, only if an item in the listbox is clicked... and I didn't succeed on the others message type either, from the Add Event Handler.
Once a window handles a message, it stops being sent to the other windows beneath it. A static window doesn't handle a mouse down, so it goes to the dialog. The other controls all handle it in some fashion (setting focus at least) so it never gets to the dialog.
You can override the OnLButtonDown handler in each of the controls to do something else once they've finished their default handling in the base class. You might also get to see the message in the PreTranslateMessage method of the dialog.
As far as getting the List Controls to sync scrolling goes, your best bet would be to intercept WM_VSCROLL by subclassing CListCtrl or perhaps by PreTranslateMessage, and then calling SetScrollInfo on the other list. If the number of items in the lists are the same, it should be fairly simple.
I have an textbox with onChange method and button to make some actions. But if I type some thing in textbox and not clicking any where , click that button, It calls onClick method and then onChange method.Or first onChange and then OnClick but I should disable all actions after that onChange method.
Add the check to your onClick() method. The onChange() for a text box is fired after a certain period of time or after you deselect your component. If you deselect your component by clicking on a button it sounds pretty natural to me to get the onClik first and then the onChange. There is no way of controlling (as far as I know) these events, except on server side.
Read this !
Keep in mind that you are developing a WEB application and not a Desktop Application. And event if the development of zk applications may look pretty similar to the desktop applications they are not and they have their limitations.
I found solution :
First for Textbox onFocus method I disable next button , and user cant click it.
Second for Textbox onBlure method I enable next button. (To be fired onBlure action user should click somewhere on window or press tab and this fires onChange action)