I am using AORangeSlider which is a subclass of UIControl that overrides the beginTracking, continueTracking and endTracking methods.
When this control is added to a viewController, which is presented in iOS 13 using the "cards style", it has very strange behavior. When sliding the control the viewController will try to swipe down, and it interrupts the slider behavior and makes it unreliable and not work correctly.
If I instead, I present the viewController with UIModalPresentationFullScreen the control works correctly.
Is there a way to allow the AORangeSlider/UIControl to work with the iOS 13 cards style of presentation and not have their touch events conflict?
Had to ask Apple about this, and they recommended the following solution, which worked:
if (#available(iOS 13.0, *)) {
for (UIGestureRecognizer *gestureRecognizer in self.navigationController.presentationController.presentedView.gestureRecognizers) {
gestureRecognizer.enabled = NO;
}
}
Related
Landscape apps made with Xcode 8.3 are letterboxed on the iPhone X, and the home bar is partially disabled, meaning that the user must swipe up to "wake it" then swipe up again to exit the app. It's this second functionality that I want to implement while taking full advantage of the screen size, so how do I replicate that feature using Xcode 9?
If I set the view controller's prefersHomeIndicatorAutoHidden() to return true, the home bar temporarily disappears, but every time the user touches the screen it comes back (a bit jarring), but it still only takes a single swipe to exit the app. I have not been able to find any other options to do what I want, but clearly it should be possible since it automatically happens for older apps.
Suggestions?
[Note crossposted on Apple Developer Forum]
This behavior is set up by implementing preferredScreenEdgesDeferringSystemGestures in a UIViewController, as follows:
- (UIRectEdge)
preferredScreenEdgesDeferringSystemGestures
{
// prevent home bar from interfering
return (UIRectEdgeTop | UIRectEdgeBottom);
}
Once this is done, the Home Bar should not be auto-hidden:
- (BOOL)
prefersHomeIndicatorAutoHidden
{
return NO;
}
In addition, in a convenient place (such as viewDidAppear:), you need to notify the system that these properties have changed:
- (void)
viewDidAppear:(BOOL) isAnimated
{
[super viewDidAppear:isAnimated];
if (#available(iOS 11.0, *))
{
[self setNeedsUpdateOfHomeIndicatorAutoHidden];
[self setNeedsUpdateOfScreenEdgesDeferringSystemGestures];
}
}
I am trying to use UIRefreshControl on a table view together with the new searchController API on navigationItem.
Now when I set hidesSearchBarWhenScrolling the "pull down to refresh" animation is not showing anymore and the refresh control just pops in at a certain point.
It appears to be a bug in UIKit (...same procedure as every year).
Did anyone find a solution for this one?
To reproduce the issue add this to a fresh iOS 11 "master/detail" sample project:
- (void)viewDidLoad {
// [setup code here]
self.refreshControl = [UIRefreshControl new];
self.navigationItem.searchController = [[UISearchController alloc] initWithSearchResultsController:nil];
self.navigationItem.hidesSearchBarWhenScrolling = NO; // <-- setting this causes jumpy UI
}
I just experienced the same problem. It definitely looks like a bug in UIKit. It would definitely be something filing a radar would be worth.
I found a very hacky way to mitigate it though:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//Fixes a bug in UIKit where the refresh control is broken when `hidesSearchBarWhenScrolling` is NO.
if (#available(iOS 11.0, *)) {
self.navigationItem.hidesSearchBarWhenScrolling = scrollView.contentOffset.y < -scrollView.adjustedContentInset.top;
}
}
Basically what's happening here is that whenever the scroll view scrolls past the top (where the refresh control would become visible), this bit of code will turn hidesSearchBarWhenScrolling back to YES. Once the user scrolls back down again, it will be set back to NO and the search bar will continue to remain visible.
Hopefully Apple will fix this in a future iOS version, but for current shipping versions, this will probably have to do.
On iOS 7, when UIAlertView is presented, its buttons seem to automatically have exclusiveTouch=YES set. This way, if you simultaneously press more than one button on alert view, nothing terribly will happen.
This is not the case on iOS 8. When I present UIAlertView with 2 buttons. And I press these 2 buttons at the same time, the app freezes.
I cannot access the UIButtons on that UIAlertView. I cannot access any of the subviews of UIAlertView for that matter.
alertView.subview returns empty array, no matter in which part of the lifecycle I call it.
I have managed to find a solution to the problem. I implement the UIAlertView's delegate method:
-(void)didPresentAlertView:(UIAlertView *)alertView
{
UIView *view = ((UIApplication*)[UIApplication sharedApplication]).keyWindow.rootViewController.presentedViewController.view;
[self setExclusiveTouchForView:view];
}
-(void) setExclusiveTouchForView:(UIView*)view
{
for (UIView *subview in view.subviews) {
subview.exclusiveTouch = YES;
[self setExclusiveTouchForView:subview];
}
}
At first it was confusing to see that neither of all the subviews inside the view variable is of class UIButton or UIControl.
It is not actually necessary to set exclusiveTouch property on all of the subviews. But I posted it like this to reduce code complexity.
It works fine. App no longer freezes when both of UIAlertView's buttons are pressed at the same time. Only one of the presses is accepted.
I made a widget for the iOS 5 notification center that implements a UISlider along with a UITapGestureRecognizer.
The gestureRecognizer works fine, but the UISlider is very unresponsive and will only move a small amount if the thumb is touched and dragged.
It will not move until another touch down. Is there a way to circumvent this limitation? (I didn't load any views above it, it works fine on the iPhone and iPod Touch).
I finally found a way to fix it.
You should set the superviews gesturerecogniser's cancelTouchesInView property to NO on viewWillAppear.
- (void)viewWillAppear {
if (deviceIsIPAD()) {
UIView *list = [[objc_getClass("SBBulletinListController") sharedInstance] listView];
for (UIGestureRecognizer *gr in list.gestureRecognizers) {
gr.cancelsTouchesInView = NO;
}
}
}
I'm trying to fix a bug that involves UIView hitTest:withEvent: being called on my view when the touches are on the UIKeyboard, but only after the app has been in the background.
It was occurring in my app with a complex view hierarchy so I reproduced it in an app with only 2 views:
1 UIView 768x1024 (fullscreen)
1 UITextView 200x200 in the upper half of the fullscreen view
The behavior is as follows:
Tapping the textview causes the fullscreen view's hitTest method to fire, the textfield becomes first responder, and then the keyboard appears all as expected. Tapping on keyboard keys works fine.
Now dismiss the keyboard.
Send the app to the background.
Then resume the app.
Make the textview first responder again. Here's the trouble, now when tapping keys on the keyboard the fullscreen view's hitTest method is firing.
I'm seeing this on an iOS 5 iPad 2. Only on device though, never in the simulator. Any idea why hitTesting could get messed up in this way? Thanks.
The issue described above seems to be caused by the keyboard's UIWindow getting stuck in a bad state. Ensuring that the keyboard window's hidden property gets set to YES (even if it is already YES) fixes the problem for me. This can be done in your UIApplicationDelegate class:
- (void)applicationWillEnterForeground:(UIApplication *)application {
// The keyboard sometimes disables interaction when the app enters the
// background due to an iOS bug. This brings it back to normal.
for (UIWindow *testWindow in [UIApplication sharedApplication].windows) {
if (!testWindow.opaque && [NSStringFromClass(testWindow.class) hasPrefix:#"UIText"]) {
BOOL wasHidden = testWindow.hidden;
testWindow.hidden = YES;
if (!wasHidden) {
testWindow.hidden = NO;
}
break;
}
}
}
The class name of the keyboard window, at least in iOS 5 with a standard US keyboard, is UITextEffectsWindow. As usual it is not a good idea to rely on undocumented class names but in the case of an OS-specific bug it works for my purposes. There could be any number of windows, including the root application window, keyboard, alerts, and other windows that your app or other frameworks have added, so don't be too inspecific.
Got the same issue here. It does happen ONLY when I hit home and return to the app.
Does not happen in the first fresh run.
And it is related to iOS5 as well.
I got the same problem, and my work around is to listen to UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, calculate the keyboard height using UIKeyboardFrameEndUserInfoKey, then in my hitTest:withEvent: method I would see whether the hit was on the keyboard "zone" or not.
Just to expand on the answer by #enzo-tran , this is what I ended up doing: I added a keyboardRect property to my UIView subclass, registered for UIKeyboardDidShowNotification and UIKeyboardDidHideNotification, and added:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (CGRectContainsPoint([self keyboardRect], point)) {
// Ignore
} else {
...
}
}
- (void)keyboardDidShow:(NSNotification *)notif {
CGRect keyboardRect;
[[[notif userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect];
keyboardRect = [self convertRect:keyboardRect fromView:nil];
[self setKeyboardRect:keyboardRect];
}
- (void)keyboardDidHide:(NSNotification *)notif {
[self setKeyboardRect:CGRectZero];
}