Can't get scrollsToTop working on iOS7 - iphone

I'm targeting iOS7 in my latest app, and tapping on the status bar doesn't seem to scroll a tableView or collectionView to the top.
I've set self.tableView.scrollsToTop = true and still nothing happens.
I know Apple significantly changed the status bar in iOS7, but did those changes break the scrollsToTop functionality?
Update
In response to a comment in one of the answers, I tested to ensure that my collection view was indeed the only scrollView on the screen, and it was:
(lldb) po [self.view recursiveDescription]
<UIView: 0x1092ddf0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x109357e0>>
| <UICollectionView: 0x11351800; frame = (0 0; 320 568); clipsToBounds = YES; opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x10966080>; layer = <CALayer: 0x109623a0>; contentOffset: {0, -64}> collection view layout: <UICollectionViewFlowLayout: 0x10940a70>
| | <UIImageView: 0x10965fa0; frame = (0 564.5; 320 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x10965ee0>> - (null)
| | <UIImageView: 0x10948f60; frame = (316.5 561; 3.5 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x10966030>> - (null)
Update #2
Not sure if it matters, but I'm using a standard iOS7 NavigationController where the navigationBar is transparent and applies a blur to my collection/tableViews as they scroll underneath.
Update #3
Figured it out. Turns out I did have more than one scrollView on the screen. My app has a left drawer menu underneath the main part of the app, and that menu has a tableView for the options. I simply set self.menuTable.scrollsToTop = false and everything worked as expected throughout the rest of the app. Didn't have to implement the scrollView Delegate methods or anything.

Do you have more than one scroll view/table view/collection view on screen? If so, only one of them can have scrollsToTop set to YES, otherwise iOS7 will not scroll any of them to the top.
You can also implement the UIScrollViewDelegate method scrollViewShouldScrollToTop: and return YES if the passed in scroll view is equal to the one that you want to scroll to the top:
- (BOOL) scrollViewShouldScrollToTop:(UIScrollView*) scrollView {
if (scrollView == self.myTableView) {
return YES;
} else {
return NO;
}
}

The short answer is there's nothing different in iOS7. As long as there isn't more than one UIScrollView loaded, your tableView or collectionView will scroll to the top when the user taps the status bar. The key here is loaded; another scrollView doesn't necessarily have to be on screen to conflict with another scrollView that is.
Sliding drawers in the left/right are very popular these days, and this was the reason for my problem. I have a menu containing my navigation options, and these are all held by a UITableView. I had to make sure that I set menuTable.scrollsToTop = false before I could get things working in the other parts of my app.

My problem was that I had a UITextView with scrollsToTop set to YES, so my UITableView wasn't responding to the gesture. In short, make check all other scrollable views.

for others :
Remember that the scroll view you are searching can also be a UIWebView..not just UITableView.
Another important thing is that it's not only about VISIBLE scrollViews, but LOADED scrollviews.
If you don't find the scrollView, you can always
insert UITableView test table, immediately when app is starting, check if it's scroll to top,
and then load more and more views, until the test table stop scrolling to top.

If your table cells are dynamic, remove the following:
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
return YES;
}
Create a new function as follows:
- (void) disableScrollsToTopPropertyOnAllSubviewsOf:(UIView *)view {
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
((UIScrollView *)subview).scrollsToTop = NO;
}
[self disableScrollsToTopPropertyOnAllSubviewsOf:subview];
}
}
Call the function above in - (void)viewDidLoad
[self disableScrollsToTopPropertyOnAllSubviewsOf:self.view];
Now enable ScrollsToTop for your table view as follows:
[myTableView setScrollsToTop:YES];

This always works for me:
[self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];

Related

View isn't rotating properly

I have this code inside a UIViewController subclass:
- (id)init {
self = [super init];
if (self) {
self.view.frame = [PDToolbox screenFrame];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.view.backgroundColor = [UIColor redColor];
}
return self;
}
The only thing I have in terms of any rotation methods is this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
Yet after the screen is rotated to landscape, doing an NSLog on the view shows this:
<UIView: 0x10061640; frame = (0 0; 320 480); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x10061670>>
I don't understand why it's doing this transform thing, and not just rotating like normal? It means any views i place on top of it after the rotation and set to be the size of the view end up at a 320x480 position.
EDIT:
People aren't understanding. So I put a view on top of it, the same size as the UIView, using:
UIView *anotherView = [UIView alloc] initWithFrame:controller.view.bounds];
[controller.view addSubview:anotherView];
If I add anotherView in in portrait, anotherView appears in portrait, with the frame 320x480.
If i add anotherView in in landscape, anotherView appears in landscape, but still with the frame 320x480, becaus that's what the controller.view's frame is still, for some unknown reason.
What does your view/controller hierarchy look like? It looks to me like something is setting a 90° rotation transformation on your view, rather than changing the view's frame. If you're not doing that yourself, it's likely a parent view or view controller.
Frame and bounds are very different things. You should read Apple's guide to View Geometry - it contains a lot of information that's been invaluable to me.
Your bounds is always going to be 320x480 because someone is setting your transform (which affects the frame, but not the bounds).
Make sure your view controller's view's superview has autoresizesSubviews set to YES and that any subviews added have appropriate autoresizingMasks.

UIScrollView initializing with two uiimageviews from system?

I have a Nib with a scrollview which i use on my controller. I have to make some constant calculations based on the scrollview.subviews count. However, surprisingly the scrollview is always starting with two uiimage views.
The scrollview at my Nib file is empty, and i have even checked the nib source code to assure there is no garbage there. I also deleted the scrollview and created another with the same result.
I am always receiving the same views there (always at, so my scrollview.subviews.count always start at 2. What could be causing this??
If I print scrollview subviews just after initializing view..
[[NSBundle mainBundle] loadNibNamed:#"TileScreen" owner:self options:nil];
NSLog(#"Started scrollview with subviews %#", _scrollView.subviews);
I receive:
Started scrollview with subviews (
"<UIImageView: 0x1dcb50; frame = (294 400; 7 7); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x1e51b0>>",
"<UIImageView: 0x1bb6a0; frame = (294 400; 7 7); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x1a9b50>>"
)
Well before I posted this question I understood the problem. I wanted to share in case someone falls into this.
The problem is that the "Show horizontal scrollbar, Show vertical scrollbar" add the mentioned UIImageViews. You can avoid this by simply unchecking the property on the IB.
If you want to show the scrollbars though, you will need to take into account these two views in your count.

Full Screen UIScrollView not working Correctly

I have an application like the photos app where the main view is a UIScrollView which takes up the full size of the screen. Also, like the photos app, when the user taps the screen there are translucent navigation, status, and tool bars which reappear / disappear.
I am having a problem setting the UIViewControllers main view as a UIScrollView and having it take up the full length of the screen. The problem is that when the navigation and status bars are shown, the UIScrollView gets pushed down by the height of the navigation and status bars (it doesn't go underneath them like it's suppose to). When the user taps the screen and the navigation / status bars disappear, then it resets itself to take up the full length of the screen like it's suppose to.
A simple work around of setting the main view as a UIView and attaching a UIScrollView on top of it works. However, I'd like to try and get this to work without any workarounds (ie adjusting the UIScrollViews contentInset, etc) because in theory it should work.
Below is the code I'm implementing:
- (void)loadView
{
self.wantsFullScreenLayout = YES;
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame: CGRectMake(0,0,320,480)];
scrollView.contentSize = CGSizeMake(320, 480);
scrollView.scrollEnabled = NO;
scrollView.contentOffset = CGPointZero;
scrollView.bounces = NO;
self.view = scrollView;
[scrollView release];
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationController.navigationBar.translucent = YES;
[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleBlackTranslucent animated: NO];
self.navigationController.toolbarHidden = NO;
self.navigationController.toolbar.barStyle = UIBarStyleBlack;
self.navigationController.toolbar.translucent = YES;
[self startTimer];
}
- (void) viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
self.navigationController.navigationBar.translucent = NO;
[[UIApplication sharedApplication] setStatusBarStyle: UIStatusBarStyleDefault animated: NO];
[self cancelTimer];
}
UPDATE: I've noticed it's the contentOffset and contentInset that are changing, not the scrollViews frame. When the bars have disappeared and the UIScrollView is the full size of the screen (as it should be), the contentOffset and contentInset are as follows:
Content Offset: {0, -20}
Content Inset: {20, 0, 44, 0}
When the bars are visible and the UIScrollView is pushed down, the contentOffset and contentInset are as follows:
Content Offset: {0, -64}
Content Inset: {64, 0, 44, 0}
I solved a similar issue on iOS7 after reading Apple's UI Transition Guide:
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/TransitionGuide/AppearanceCustomization.html
It turns out UIViewController has a automaticallyAdjustsScrollViewInsets boolean property.
Default is true, disabling this made my UIScrollView fill up the entire window, instead of being pushed down.
I spoke with DTS about this and they said this is the designed behavior and recommended to attach the UIScrollView to a parent UIView.
Try be setting:
self.view.frame = CGRectMake(0.f, -44.f, 320.f 480.f);
It may not size as you wish, but you can adjust it...

UIView changing size

I have a view that I'm creating in the loadview like this:
- (void)loadView {
UIView *mainView = [[UIView alloc] initWithFrame:[UIApplication sharedApplication].keyWindow.bounds];
self.view = mainView;
[mainView release];
}
So, if I print the view I got the result:
>
OK, that's what I wanted, 767x1024
The problem is, if I call this method:
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationSlide];
and present a modalViewController and then dismiss it, on my previous view controller, printing the method viewDidAppear and viewWillAppear I got this:
viewWillAppear:
[<UIView: 0x4e946a0; frame = (0 0; 768 1024); layer = <CALayer: 0x4e946d0>>]
viewDidAppear:
[<UIView: 0x4e946a0; frame = (0 0; 768 1004); layer = <CALayer: 0x4e946d0>>]
Why the view size is changing by 20? I know that has something to do with the status bar, but can't figure it out.
Thanks.
You are right. Status bar default size is 20 pixel in height. UIViewController automatically adjust child view size as status bar appears. According to apple documentation "When a view controller is displayed on screen, its root view is typically resized to fit the available space, which can vary depending on the window’s current orientation and the presence of other interface elements such as the status bar." — http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html

Strange UIImageView in UIScrollView

Very strange behavior, there is a round dot in the center of the screen using this code, and a UIScrollview with nothing inside in a nib. I expect that UIScrollview should be empty. The dot blurs and disappears when I scroll the screen.
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *subviews = [closetScroll subviews];
UIImageView *strange=[subviews objectAtIndex:0];
strange.center = CGPointMake([[UIScreen mainScreen] bounds].size.width/2, [[UIScreen mainScreen] bounds].size.height/2);
strange.alpha=1;
NSLog(#"%#",subviews);
}
The console output is:
<UIImageView: 0x4b1f780; frame = (380.5 508.5; 7 7); opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x4b1f820>
Does anyone know why?
After magnifying the UIImageView and tweaking with configurations, I have come to conclude that the UIImageView is actually the scroll bar, and if horizontal and vertical scroll is enabled, an "empty" UIScrollview has two subviews inside.
I had the same issue. Calculating the number of subviews can be very deceptive, because of this "feature".
Start the application and count the number of subviews, this will be 1. Now, use you mouse in the simulator or finger on the device and swipe from the right to the left. Count the number of subviews. The number will be 2.
I can deduce nothing else than that this extra UIImageView is produced by Cocoa Touch to render the background in the right color when "bouncing" beyond the end of the UIScrollView's bounds.