UIScrollView, Pull down effect similar to CalcBot - objective-c

I'm trying to make a UI similar to CalcBot where you can pull down the view controller with your finger and it reveals a hidden background view.
I think this is done with the UIScrollView but cannot work out the exact method to have for example 2x Horizontal views with one of them having a vertical.
I do have the horizontal scroll working fine, i'm just stuck with how I can make screen 1 pull down with a vertical scroller.
Any tips or pointers would be appreciated.
Thanks Aaron

You can have nested scrollviews to get scrolling working in both directions. Let's have a vertical scroller inside a horizontal scroller (though you can very well do it the other way round too), so views 3 and 1 get into a vertical scrollview, which in turn gets inside a horizontal scroller along with view 2.
You could create such a view hierarchy in code (probably in the main viewcontroller's viewDidLoad). The following snippet assumes view1, view2 and view3 as webviews.
CGRect bounds = self.view.bounds;
// main guy is a horizontal scroller
UIScrollView *hScrollView = [[UIScrollView alloc] initWithFrame:bounds];
hScrollView.contentSize = CGSizeMake(bounds.size.width * 2, bounds.size.height);
hScrollView.delegate = self;
[self.view addSubview:hScrollView];
// the horizontal scroller contains a vertical scroller
UIScrollView *vScrollView = [[UIScrollView alloc] initWithFrame:bounds];
vScrollView.contentSize = CGSizeMake(bounds.size.width, bounds.size.height * 2);
vScrollView.delegate = self;
[hScrollView addSubview:vScrollView];
// add view3 and view1 to the vertical scroller
UIWebView *view3 = [[UIWebView alloc] initWithFrame:bounds];
[view3 loadHTMLString:#"<h1>3</h1>" baseURL:nil];
[vScrollView addSubview:view3];
UIWebView *view1 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, 0, bounds.size.height)];
[view1 loadHTMLString:#"<h1>1</h1>" baseURL:nil];
[vScrollView addSubview:view1];
vScrollView.contentOffset = CGPointMake(0, bounds.size.height);
// add view2 to the horizontal scroller
UIWebView *view2 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, bounds.size.width, 0)];
[view2 loadHTMLString:#"<h1>2</h1>" baseURL:nil];
[hScrollView addSubview:view2];
// enable paging in both directions
hScrollView.pagingEnabled = TRUE;
vScrollView.pagingEnabled = TRUE;
That would have you scrolling in both directions but that would also let you scroll-right from view3 to view2. If you want to prevent that, you should disable scrolling on hScrollView whenever the vScollView is showing view3. You can do that in scrollViewDidEndDecelerating: in the delegate of vScrollView. Something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.verticalScrollView) {
self.horizontalScrollView.scrollEnabled = (self.verticalScrollView.contentOffset.y > self.view.bounds.origin.y);
}
}

Related

How to pass touch events from one view to another

I have a UIScrollView with a UIView subview, which has a UITableView subview. What I want is for the container scroll view to scroll when the user scrolls on the UITableView. Is there a way to pass the touch event from the child UIScrollView to the container UIScrollView? Thanks for you help. Here is the code for the scroll view. The parent scroll view is created in code, and the subviews are created in IB.
CGFloat offset = ([[UIDevice currentDevice].systemVersion floatValue] >= 7.0) ? 64.0 : 44.0;
self.parentScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0, offset, 320.0, self.view.frame.size.height - offset)];
self.parentScrollView.delegate = self;
self.parentScrollView.bounces = NO;
self.parentScrollView.contentInset = UIEdgeInsetsMake(self.header.frame.size.height - offset, 0, 0, 0);
self.parentScrollView.delaysContentTouches = NO;
[self.view addSubview:self.parentScrollView];
[self.contentView.tableView registerNib:[UINib nibWithNibName:#"PodcastCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"PodcastCell"];
[self.contentView.tableView registerNib:[UINib nibWithNibName:#"DescriptionCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:#"DescriptionCell"];
self.contentView.delegate = self;
self.contentView.tableView.delegate = self;
self.contentView.tableView.dataSource = self;
[self.parentScrollView addSubview:self.contentView];
self.parentScrollView.contentSize = CGSizeMake(320.0, self.contentView.frame.size.height);
Sorry I didn't explain this very well. I need the parent UIScrollView to scroll until the child UITableView reaches the top of the parent, at which point the parent UIScrollView should stop scrolling, and the child UItableView should start scrolling. Does that make sense?
Implement scrollViewDidScroll: in your UITableView, calculate the contentOffset then call setContentOffset:animated: from your container view.

How to move scrollView down 44x to make room for NavBar

I am using the following code to create my scrollview. I would like to move the scrollView down 44px to make room for my nav bar.
scroll = [[UIScrollView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
scroll.backgroundColor = [UIColor clearColor];
scroll.delegate = self;
image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Menu.png"]];
scroll.contentSize = image.frame.size;
[scroll addSubview:image];
scroll.minimumZoomScale = scroll.frame.size.width / image.frame.size.width;
scroll.maximumZoomScale = 2.0;
[scroll setZoomScale:scroll.minimumZoomScale];
UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleDoubleTap:)];
[doubleTap setNumberOfTapsRequired:2];
[scroll addGestureRecognizer:doubleTap];
self.view = scroll;
any help is appreciated.
Your code is correct, to manually create a frame do:
scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0,44,320,480)];
:)
This is for iPod and iphone View but this will let the scroll start at 44px
Cant you just add a scroll view in in interface builder as an outlet, place it underneath your nav bar.
Declare scroll view in .h
in .m under ViewDidLoad
[scroll setScrollEnabled:YES];
[scroll setContentSize: CGSizeMake(320, 1000)]; ////or whatever size you want here
You probably want to init it with a different frame... but it depends on where/how this nav bar is being created. If you have a view controller, and these are all subviews of the view controller's view, then you should be creating the objects in viewDidLoad, then just create the nav bar first, using self.view.bounds to obtain the initialization width. I assume you'll want to put UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin as the autoResizingMask here. Then if the scrollview is the rest of the view below the nav bar, you can create the frame for it using
CGRect scrollFrame = CGRectInset(self.view.bounds, 0, navbar.bounds.size.height)
Put an autoresizingMask of UIViewAutoresizingFlexibleWidth | UIViewAutoResizingFlexibleHeight on the scrollview.
If you are creating the views in a different place/way, then some of that might need modification. I was assuming your view is a nav bar at the top, x pixels tall (44 in this case but it doesn't and shouldn't matter in the context of setting the scrollview frame). and then a scrollview that fills the rest of the view.

UIScrollView metro theme

I am attempting to create a "metro" styled UIScrollView. It is similar to how iTunes app handles panels in the new ios version which wont be named.
I can't figure out how to have my views layout/scroll so that the next view in the sequence shows up. I've tried all sorts of things like keeping the contentSize the screen width but moving each view over -10ish so it will show up like above. I've tried making scrollView whose bounds were smaller than the screen so it would show the part of the next view. Nothing works.
Here is diagram of what I'm trying to do:
It seems extremely trivial on paper but I can't seem to get it work.
I'm not sure if I'm misinterpreting your requirements - but this might be a starting point to see how you could set it up:
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect viewBounds = self.view.bounds;
CGRect scrollViewFrame = CGRectMake(0, 0, floorf(CGRectGetWidth(viewBounds) / 2.2), CGRectGetHeight(viewBounds));
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:scrollViewFrame];
scrollView.center = self.view.center;
scrollView.contentSize = CGSizeMake(CGRectGetWidth(viewBounds) * 3, CGRectGetHeight(viewBounds) * 3);
scrollView.pagingEnabled = YES;
scrollView.clipsToBounds = NO;
UIPanGestureRecognizer *gestureRecognizer = scrollView.panGestureRecognizer;
[self.view addGestureRecognizer:gestureRecognizer];
for (int i = 0; i < 3; i++) {
CGRect frame = CGRectMake(10.f + (i * CGRectGetWidth(scrollView.bounds)), 10.f, CGRectGetWidth(scrollView.bounds) - 20.f, (CGRectGetHeight(scrollViewFrame) * 3) - 20.f);
UIView *view = [[UIView alloc] initWithFrame:frame];
view.backgroundColor = [UIColor redColor];
[scrollView addSubview:view];
}
[self.view addSubview:scrollView];
}
Literally just put this in an empty viewController's viewDidLoad:
The key things to note are
contentSize needs to be wide enough for all the panels
clipsToBounds should be NO so you can see the additional views
The bounds of the scrollview is essentially the main view port
pagingEnabled should be set
I've grabbed the panGestureRecognizer from the scrollview and attached it to the containing view instead so that panning is detected in the bounds of the containing view (which is larger) otherwise you are restricted to only detecting scrolls within the scrollviews bounds

MKMapView panning causes parent UIScrollView to scroll

I have a MKMapView added to a UIScrollView. I thought that the expected functionality would be that panning the map would cause the map to move, but it's parent UIScrollView would not scroll. i.e. if you were panning the map, the scrollview would stay in it's current position.
This is my code:
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.width, self.height)];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[self.view addSubview:scrollView];
double mapHeight = 200;
if([AppDelegate isRunningOniPad])
mapHeight = 400;
map = [[MKMapView alloc] initWithFrame:CGRectMake(0, 200, self.width, mapHeight)];
[map setExclusiveTouch:NO];
[scrollView map];
I've tried setting exclusive touch to YES and to NO, but it doesn't seem to have any effect.
Is there a way to get the behaviour working so that panning the map will stop the UIScrollView from moving? I've tried changing map to a UIScrollView instead of an MKMapView too, but that had the same behaviour - so I doubt that this is MKMapView specific.
The expected outcome is correct, the scroll view should remain stationary when the Map View is touched, as this should receive the touch events before they are passed up the responder chain to the scroll view.
I have quickly tested the above code in an empty project, and this is working as expected with a couple of alterations to the creating of frame & sizes, iPad size detection, and adding the map as a sub-view of the scroll view.
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
// Make the content size large to allow scroll view to move
[scrollView setContentSize:CGSizeMake(1000, 2000)];
[self.view addSubview:scrollView];
double mapHeight = 200;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
mapHeight = 400;
}
MKMapView *map = [[MKMapView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, mapHeight)];
[map setExclusiveTouch:NO];
[scrollView addSubview:map];
In case it helps anyone,
Adding a MKMapView to a UIScrollView should work as expected. In my situation, it wasn't working because my view hierarchy was setup incorrectly.
If you're having problems look at how you are adding sub views (ie [self.view addSubview:mapView]. Make sure the MKMapView is properly in the UIScrollView's hierarchy.
Also, I really like revealapp.com to inspecting the view hierarchy (and no I'm not associated with them ;)

NSScrollView doesn't display vertical scrollbar after adding content

I've created an NSScrollView which itself contains a NSClippedView as content view (this is all default, created by IB). Inside the contents view there is the (default) document view.
This NSScrollView has horizontal scroller disabled and vertical enabled and, most importantly auto hide scrollers enabled.
When I add new views (via code, runtime) to the document view the scroller does not unhide automatically, until the moment I vertically resize the window (and which in turn resizes the scrollview as well). 1px is enough. Just the new painting of the window seems enough.
What I am looking for is triggering this by code: so when I add views to the scrollviews' content view I would like the scrollbar to appear.
int numberOfChildViews = 10; //hard coded for example here
int childViewHeight = 80; //same as above
NSRect rect = NSMakeRect(0, 0, [[self.scrollView contentView] bounds].size.width, [numberOfChildViews*childViewHeight);
[[self.scrollView documentView] setFrame:rect];
[[self.scrollView documentView] setBounds:rect]; //just added to make sure
Then I added the custom views to the document view, like:
for (int i=0; i<numberOfChildViews; i++) {
NZBProgressViewController *item = [nzbProgressArray objectAtIndex:i];
int y=i*[[item view] bounds].size.height;
rect= NSMakeRect(0, y, [[scrollView contentView] frame].size.width, [[item view] bounds].size.height);
[[item view] setFrame:rect];
currentPosition++;
}
I am using a FlippedView so the origin will be displayed in left-top, like so:
#interface NSFlippedClipView : NSClipView {
}
#implementation NSFlippedClipView
- (BOOL)isFlipped {
return YES;
}
- (BOOL)isOpaque {
return YES;
}
#end
And added the following code to the awakeFromNib
NSFlippedClipView *documentView = [[NSFlippedClipView alloc] init];
[documentView setAutoresizingMask:NSViewWidthSizable];
[documentView setBackgroundColor:[self.scrollView backgroundColor]];
[self.scrollView setDocumentView:documentView];
[documentView release];
The scrollbars should become visible as soon as the document view is resized to be larger than the current viewport of the scroll view. Are you resizing the document view when you add your subviews to it?
Ahh, it's my own bad. For future reference: if you want to move the origin (0,0) to left-top use a NSView instead of NSClippedView extended class with IsFlipped method overriden to YES.
Thanks irsk for answering.