Waiting for viewDidAppear: call on UICompatibilityInputViewController - earlgrey

I have a UITableView which contains 2 cells, each contain 2 UITextField's. Because the cells are dynamically created I use accessibility labels and values to select the elements like so:
id<GREYMatcher> firstNameMatcher = grey_allOf(grey_accessibilityLabel(#"First name"),
grey_accessibilityValue(#"Adult 1"), nil);
id<GREYMatcher> lastNameMatcher = grey_allOf(grey_accessibilityLabel(#"Last name"),
grey_accessibilityValue(#"Adult 1"), nil);
id<GREYMatcher> firstNameMatcher1 = grey_allOf(grey_accessibilityLabel(#"First name"),
grey_accessibilityValue(#"Adult 2"), nil);
id<GREYMatcher> lastNameMatcher1 = grey_allOf(grey_accessibilityLabel(#"Last name"),
grey_accessibilityValue(#"Adult 2"), nil);
[[EarlGrey selectElementWithMatcher:firstNameMatcher]
performAction:grey_typeText(#"Test")];
[[EarlGrey selectElementWithMatcher:lastNameMatcher]
performAction:grey_typeText(#"Test")];
[[EarlGrey selectElementWithMatcher:firstNameMatcher1]
performAction:grey_typeText(#"Test")];
[[EarlGrey selectElementWithMatcher:lastNameMatcher1]
performAction:grey_typeText(#"Test")];
Adult 1 fields are in the first cell and Adult 2 in the second cell.
The "test" appears in the first 2 text fields, in the first cell no problems. The EarlGrey then gives the following error:
EarlGrey tried waiting for 5.0 seconds for the application to reach an idle state. It is now forced to clear the state of GREYAppStateTracker, because the test might have caused the application to remain in non-idle state indefinitely.
Full state tracker description: Waiting for viewDidAppear: call on this view controller. Please ensure that this view controller and its subclasses call through to their super's implementation
Waiting for CAAnimations to finish. Continuous animations may never finish and must be stop explicitly. Animations attached to hidden view may still be executing in the background.
Full state transition call stack for all elements:
<UICompatibilityInputViewController:0x7fe9394870d0> => Waiting for viewDidAppear: call on this view controller. Please ensure that this view controller and its subclasses call through to their super's implementation
I have managed to get it to type into the first text field of the second cell (accessibility label: "Adult 2", value: "First name) by hiding the keyboard (cmd+k). But it won't get any further, no matter how much I show/hide the keyboard.

#JFoulkes This is fixed now - https://github.com/google/EarlGrey/pull/431

Related

How to force the order of UIKit pop up menu button items?

I have a couple of UIKit pop-up menu buttons with identical menu items on the same screen in a Swift app. The buttons are built by calling a function that uses an array of strings to create the list of menu items.
The problem is that depending on the button's vertical position on the screen, the menu items may appear in the order specified by the function, or reversed. If the button is in the upper half of the screen, the menu items are listed in the correct order. If the button is in the lower half of the screen the menu items are listed in reverse order.
I would prefer the menu items to appear in the same order regardless of the button's position on the screen. I could check the button location and have the menu creation function reverse the order, but that seems kind of clunky. I am hoping there's a cleaner way to override this behaviour.
The code and array used to create the button menus:
let buttonMenuItems = ["Spring","Summer","Autumn","Winter"]
func createAttributeMenu(menuNumber: Int)->UIMenu {
var menuActions: [UIAction] = []
for attribute in buttonMenuItems {
let item = UIAction(title: attribute) { action in
self.updateMenu(menuID: menuNumber, selected: attribute)
}
menuActions.append(item)
}
return UIMenu(title: "", children: menuActions)
}
The result is this:
Versions I'm using now in testing: Xcode 14.1, iOS 16.1, but I have seen this behaviour on earlier versions as well. (back to iOS 14.x)
Starting with iOS 16, there is a .preferredMenuElementOrder property that can be set on the button:
case automatic
A constant that allows the system to choose an ordering strategy according to the current context.
case priority
A constant that displays menu elements according to their priority.
case fixed
A constant that displays menu elements in a fixed order.
Best I can tell (as with many Apple definitions), there is no difference between .automatic and .priority.
From the .priority docs page:
Discussion
This ordering strategy displays the first menu element in the UIMenu closest to the location of the user interaction.
So, we get "reversed" order based on the position of the menu relative to the button.
To keep your defined order:
buttonNearTop.menu = createAttributeMenu(menuNumber: 1)
buttonNearBottom.menu = createAttributeMenu(menuNumber: 2)
if #available(iOS 16.0, *) {
buttonNearBottom.preferredMenuElementOrder = .fixed
buttonNearTop.preferredMenuElementOrder = .fixed
} else {
// out of luck... you get Apple's "priority" ordering
}

Looping through entities which describe what action should be taken on screen after keypresses

Please forgive me if I don't describe this question too well, I am new to programming MacOS apps using Swift. I know the way I'm going about this is probably wrong and I just need someone to tell me the right way.
My main app screen
I have a Core Data application that stores an ordered list of entities called Items. These Items are intended to describe a single step in an activity that describes what should happen on screen. If you know the Mac application QLab each Item is like a single cue in QLab.
I have created an Activity class that is designed to read through each Item to determine the Item type and it's related information. Once the Item type has been determined the Activity class needs to present a View with information related to that particular Item and then wait until the user presses the right arrow key to then proceed to the next Item in the Core Data store where the process repeats until all Items have been read. Each time a new Item is read in the loop, the information on the screen should change after the user presses the right arrow each time.
The problem is that I don't know exactly how the best way of going about this should be programatically speaking. I have the code that retrieves the array of Items as an NSFetchRequest:
let moc = (NSApplication.shared.mainWindow?.contentViewController?.representedObject as! NSPersistentDocument).managedObjectContext!
let fetchRequest : NSFetchRequest = Item.fetchRequest()
do {
let items = try moc.fetch(fetchRequest)
print("Found " + String(items.count) + " items to use in the activity.")
for item in items {
print(item.itemType)
// How do I pause this loop for a user keypress after using data from this Item to display?
}
} catch {
print("Error retrieving Items")
}
I can retrieve the keydown event using NSEvent.addLocalMonitorForEvents(matching: .keyDown) and I'm also able to create View Controllers to display the information on a second screen. I just don't know how I should create the 'main loop', so to speak, so that information is displayed and then the app waits until the user presses a key to proceed...
I can share my project code if more information is needed and many thanks to anyone who can enlighten me... :)
You could try using a NSPageController. In your NSPageController you add a ContainerView which will display the ViewControllers that display information for each item. Each ViewController will need a storyboard identifier, e.g. ViewControllerItem1.
Your NSPageController class must conform to the NSPageControllerDelegate protocol and contains an array of ViewControllers to display.
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
arrangedObjects = ["ViewControllerItem1", "ViewControllerItem2", "...","ViewControllerItemN" ]
}
Note about arrangedObjects from the NSPageController documentation: An array containing the objects displayed in the page controller’s view.
Then you implement NSPageControllers viewControllerForIdentifier to return the ViewController that you currently want to display in the ContainerView.
func pageController(_ pageController: NSPageController, viewControllerForIdentifier identifier: String) -> NSViewController {
switch identifier {
case "ViewControllerItem1":
return mainStoryboard().instantiateController(withIdentifier:"ViewControllerItem1") as? ViewControllerItem1
case "...":
default:
}
}
In your action handler for the key down event you implement.
self.navigateForward(sender) or self.navigateBack(sender)
I also implemented this method but I don't remember whether it was required.
func pageControllerDidEndLiveTransition(_ pageController: NSPageController) {
self.completeTransition()
}

Why is the ionic v1 event `beforeLeave` returning data from after it has left the view

I have an app with two tab views, view 1 and view 2. When I switch to view 2, I need to do some things in view 1, before it switches to view 2. As part of this, I have the following code.
$scope.$on('$ionicView.beforeLeave', (event) => {
const stateName = $ionicHistory.currentStateName()
console.log('beforeLeave', stateName)
})
When I switch to view 2, I expect the console.log to print view 1 as the stateName, however it prints view 2, which is counterintuitive because I would expect to return the state before I leave the view ie beforeLeave.
Is there perhaps some other event I need to listen to?
So it looks like the event $stateChangeStart does the trick that I am looking for, and what I would expect beforeLeave to do.

Swift UIAlertController fire once per row select textfield edited

I have a UIPickerView with an if statement in selectedRowInComponent that checks the value of a textField. If the user inputed number in the textfield is greater than 10, an alert is called. All is working well except I'd like to have the alert only fire a single time after the row is selected and the field is edited. As it stands, the alert is called every time the field is updated/edited with a value greater than 10. The initial alert is sufficient. Any tips on how I can accomplish this? Thanks!
if (inputField.text! as NSString).doubleValue > 10 {
SweetAlert().showAlert("Number is greater than 10", subTitle: "Please select number less than 10", style: AlertStyle.CustomImag(imageFile: "alertimage.png"))
}
Quick and Dirty:
Create a bool variable publicly available in your viewController and initialize it with false (lets call the variable 'alertAlreadyShown')
Then just make an if statement about that bool in your pickerView method
if (inputField.text! as NSString).doubleValue > 10 {
if (alertAlreadyShown == false) {
SweetAlert().showAlert(...)
alertAlreadyShown = true
}
}
But don't forget to reset the bool at appropriate times to enable the alert again
There's two ways you could go about this. One would be (and the most user friendly in my opinion) only show values of less than ten in your picker view. The other option would be to set a bool for whether or not the alert had been displayed, and add that as a check to your if statement.

Xcode update label text from other class

Few days back this question was about changing the label text from an other class. I changed a few things around:
I now have a function setLabelText:
- (void)setLabelText:(NSString*)text{
//myLabel.text = text;
[myLabel performSelectorOnMainThread : # selector(setText : ) withObject:text waitUntilDone:YES];
NSLog(#"class:%#, label:%#", self, myLabel);
}
Both self and label are filled, but the display is not showing the correct text..
I'm calling this function from an other class, if I change my label text from a button on the viewcontroller it changes correctly.
EDIT:
I have the label changing now, every time when the class is done with it's functions the label edits the text. Although.. this is always the last set value, I need it to change every time it gets into a for loop, how can I get this to work? Seems it doesn't update the viewController while the app is doing it's for loop