UIAutomation : Cancel button on Alert view is tapped without actually doing it - ios

I'm facing this weird problem in UIAutomation.
I am checking an alert. In that, I am trying to log alert title and alert message. My code for this is:
UIATarget.onAlert = function onAlert(alert) {
UIALogger.logMessage("alert Shown");
UIALogger.logMessage(frontApp.alert().name());
UIALogger.logMessage(frontApp.alert().staticTexts()[1].value());
}
var target = UIATarget.localTarget().frontMostApp().mainWindow();
target.scrollViews()[0].buttons()["saveB"].tap();
UIATarget.localTarget().delay(2);
I am not tapping on cancel button in the alert to dismiss it. But, it is getting tapped automatically. I don't know why. Even in the logMessages, I see
target.frontMostApp().alert().cancelButton().tap()
this line getting executed automatically. I don't have this line anywhere in my script file. Is it a bug in iOS?

The cancel button on an alert is always tapped to keep the application from blocking unless the onAlert callback returns true. By returning true, you are telling the alert handling mechanism that you will handle tapping the appropriate button to dismiss the alert.
Change your alert callback to look like this:
UIATarget.onAlert = function onAlert(alert) {
UIALogger.logMessage("alert Shown");
UIALogger.logMessage(frontApp.alert().name());
UIALogger.logMessage(frontApp.alert().staticTexts()[1].value());
return true; // <-- Adding this line
}
Conversely, returning false or leaving out a return value altogether signals to the alert handling mechanism that the cancel button should be tapped.

Related

How to use alert dialog when pressing back button?

I want to show alert dialog when pressing back button. But I'm facing a problem.
This is my code:
BackHanlder{
AlertDialogComponent()
}
And I got this error:
#composable invocations can only happen from the context of an
#composable function
Any help?
To show alert dialog you have to keep the boolean value as state, and change it in back handler
var showAlertDialog by remember { mutableStateOf(false)}
BackHandler {
showAlertDialog = true
}
if(showAlertDialog){
AlertDialog(//)
}

tvOS: Focus not moving correctly

I have a UIView with two buttons on it. In the MyView class I have this code:
-(BOOL) canBecomeFocused {
return YES;
}
-(NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments {
return #[_editButton, _addButton];
}
-(IBAction) editTapped:(id) sender {
BOOL editing = !tableViewController.editing;
[_editButton setTitle:editing ? #"Done" : #"Edit" forState:UIControlStateNormal];
_addButton.hidden = !editing;
[tableViewController setEditing:editing animated:YES];
}
The basic idea is that the user can move the focus to the edit button, which can then make the Add button appear.
The problem started because every time I tapped the edit button, focus would shift to the table view. I would actually like it to move to the Add button. I also want it so that when editing it deactivated, the edit button keeps the focus. but again it's shifting down to the table view.
So I tried the above code. This works in that focus can move to the view and on to the button. But once it's there, I cannot get it to move anywhere else.
Everything I've read says just override preferredFocusEnvironments but so far I've not been able to get this to work. Focus keeps going to a button then refusing to move anywhere else.
Any ideas?
If anybody is facing this issue, Just check if you are getting the following debug message printed in the console.
WARNING: Calling updateFocusIfNeeded while a focus update is in progress. This call will be ignored.
I had the following code :
// MARK: - Focus Environment
var viewToBeFocused: UIView?
func updateFocus() {
setNeedsFocusUpdate()
updateFocusIfNeeded()
}
override var preferredFocusEnvironments: [UIFocusEnvironment] {
if let viewToBeFocused = self.viewToBeFocused {
self.viewToBeFocused = nil
return [viewToBeFocused]
}
return super.preferredFocusEnvironments
}
I was calling the updateFocus() method multiple times while viewToBeFocused was either nil or some other view. Debugging the focus issues mainly between transition is really difficult. You should have patience.
Important to note: This depends on your use case, but if you want to
update the focus right after a viewcontroller transition (backward
navigation), You might have to set the following in viewDidLoad:
restoresFocusAfterTransition = false // default is true
If this is true, the view controller will have the tendancy to focus the last focused view even if we force the focus update by calling updateFocusIfNeeded(). In this case , since a focus update is already in process, you will get the warning as mentioned before at the top of this answer.
Debug focus issue
Use the following link to debug the focus issues: https://developer.apple.com/documentation/uikit/focus_interactions/debugging_focus_issues_in_your_app
Enable the focus debugger first under Edit scheme > Arguments passed on launch:
-UIFocusLoggingEnabled YES
This will log all the attempts made by the focus engine to update the focus. This is really helpful.
You can override the preferredFocusEnviromnets with the following logic:
-(NSArray<id<UIFocusEnvironment>> *)preferredFocusEnvironments {
if (condition) {
return #[_editButton];
}
else {
return #[_addButton];
}
}
After setting it, you can call
[_myView setNeedsFocusUpdate];
[_myView updateFocusIfNeeded];
The condition could be BOOL condition = tableViewController.editing; or sg like that.
If that now works, you can call it with a delay (0.1 sec or so).

Waiting for user to press a button in Swift 2

In one function in my Swift code, I need to wait until user presses a button. Otherwise, I just do nothing. How do I do this? Maybe button press triggers some event I can catch.
P.S. Due to structure of my code, doing what I want on this button press (like #IBAction) is not an option!
Declare a global boolean value such as var buttonPressed = false and then, in the IBAction of the button (When you click it):
if buttonPressed = false{
buttonPressed = true
}
And in the method you want to call when the button is pressed, just ask if buttonPressed is true.
Try that and tell me if it worked
You can set the user interaction to false by default of your main UIView (usually the UIViewController view)
self.view.userInteractionEnabled = false
and when you have the action of button you set the user interaction to true.
self.view.userInteractionEnabled = true
I might be too late but personally, there are two way you can achieve this.
Add Observer
Use delegate pattern so whenever user tap a button you notify your delegate

Swift: Prevent ABPeoplePickerNavigationController from Closing

I'd like to figure out a way so that, if the user presses the "Cancel" button (which I don't believe can be removed) in an ABPeoplePickerNavigationController, the view controller either doesn't close, or will be automatically reopened.
For example, given the following:
var picker = ABPeoplePickerNavigationController()
picker.peoplePickerDelegate = self
self.presentViewController(picker, animated: true, completion: nil)
I'd like to be able to do something like:
if (self.presentedViewController != picker && !userContinuedPastPicker) {
//where userContinuedPastPicker is a boolean set to false
//in a delegate method called when the user clicks on an a contact
//(meaning the user didn't press the cancel button but instead clicked on a contact)
//create and present a UIAlertAction informing the user they must select a contact
//present picker again
self.presentViewController(picker, animated: true, completion: nil)
}
This doesn't work; however, because the if statement won't "wait" until the user has pressed the cancel button or pressed a contact.
I'm not sure there is a way to remove the cancel button, or prevent it from working, but you could respond to the func peoplePickerNavigationControllerDidCancel(_ peoplePicker: ABPeoplePickerNavigationController!) delegate to handle the case where the cancel button is pressed.
I would recommend rather than immediately reopening the picker, you open an alert telling the user they need to pick someone, then open it back up from there. It may feel broken if they cancel and it immediately opens back up.
Reference
edit:
Presenting an alert or the picker probably needs to be delayed long enough for the previous picker to close. dispatch_after

iOS: How to prevent multiple taps of button from creating multiple new instances

Just noticed something peculiar with my app. Whenever I tap one of the bar buttons to open a popover viewcontroller, if I tap it again it simply opens another instance of that vc (I can keep doing this).
How do I stop this? Should I use a boolean to disable the button when the boolean is active and then somehow reset it when the user closes the VC by other means (such as tapping part of the screen that isn't the same VC)?
Tried the boolean suggestion:
In my prepareForSegue method I have the following:
if(isActive==false){
InformationViewController *informationViewController = [segue destinationViewController];
informationViewController.delegate = self;
isActive = true;
}
This may no longer be important, but I would recommend the boolean solution you proposed with one modification. If you move the
isActive = true;
statement to viewWillAppear, I'm pretty sure the button will remain disabled until the modal view closes.
Yes, I would suggest using a global bool value. Set the variable to true when the button is pressed.
In the function that creates the instance, check to make sure that the variable is false before creating the instance.
Once the instance is deleted, set the variable back to false.
Psuedo-code (in C++):
bool isActive = false;
void CreateInstance()
{
if (isActive == false)
{
-- code
isActive = true;
}
}
void InstanceDestroyed()
{
-- code
isActive = false;
}
You could set the button to disable once the view appears, and then add code in your popover view to:
a) send a notification using Notification Center once the popover is dismissed to be "caught" by the view that holds the button and re-enable the button, or...
b) use the delegation pattern using a protocol to handle the re-enabling of the button once the popover view is dismissed.
These methods might require a little bit more work, but I try not to use any global variables in a MVC pattern.

Resources