views:

3895

answers:

4

Mobile Safari allows you to switch pages by entering a sort of UIScrollView horizontal paging view with a page control at the bottom.

I am trying to replicate this particular behavior where a horizontally scrollable UIScrollView shows some of the next view's content.

The Apple provided example: PageControl shows how to use a UIScrollView for horizontal paging, but all views take up the whole screen width.

How do I get a UIScrollView to show some content of the next view like mobile Safari does?

+24  A: 

A UIScrollView with paging enabled will stop at multiples of its frame width (or height). So the first step is to figure out how wide you want your pages to be. Make that the width of the UIScrollView. Then, set your subview's sizes however big you need them to be, and set their centers based on multiples of the UIScrollView's width.

Then, since you want to see the other pages, of course, set clipsToBounds to NO as mhjoy stated. The trick part now is getting it to scroll when the user starts the drag outside the range of the UIScrollView's frame. My solution (when I had to do this very recently) was as follows:

Create a UIView subclass (i.e. ClipView) that will contain the UIScrollView and it's subviews. Essentially, it should have the frame of what you would assume the UIScrollView would have under normal circumstances. Place the UIScrollView in the center of the ClipView. Make sure the ClipView's clipsToBounds is set to YES if its width is less than that of its parent view. Also, the ClipView needs a reference to the UIScrollView.

The final step is to override - (UIView *)hitTest:withEvent: inside the ClipView.

- (UIView *) hitTest:(CGPoint) point withEvent:(UIEvent *)event {
  if ([self pointInside:point withEvent:event]) {
    return scrollView;
  }
  return nil;
}

This basically expands the touch area of the UIScrollView to the frame of its parent's view, exactly what you need.

Another option would be to subclass UIScrollView and override its - (BOOL)pointInside:(CGPoint) point withEvent:(UIEvent *) event method, however you will still need a container view to do the clipping, and it may be difficult to determine when to return YES based only on the UIScrollView's frame.

Ed Marty
Great answer! It worked perfectly. Thank you so much.
Jonathan Sterling
Omg I have been trying to do this for days. Thank you so much!
Kevin
Great answer, I should have thought of this!
DevDevDev
Thanks Ed. I went with a `UIScrollView` subclass for lazy loading of views (in `layoutSubviews`), and used a `clippingRect` to do the `pointInside:withEvent:` testing. Works really well, and no additional container view required.
ohhorob
+7  A: 

The ClipView solution above worked for me, but I had to do a different -[UIView hitTest:withEvent:] implementation. Ed Marty's version didn't get user interaction working with vertical scrollviews I have inside the horizontal one.

The following version worked for me:

-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
    UIView* child = nil;
    if ((child = [super hitTest:point withEvent:event]) == self)
     return self.scrollView;  
    return child;
}
Juri Pakaste
This is a good modification-- thanks.
Michael Grinich
A: 

Thanks to Ed and Juri on this one. Seems strange to me that there's no built-in support for defining page width in the UIScrollView, to dictate how pageEnabled functions, but hey ho maybe in a future API update!

Ed yours was the first approach I tried but as Juri pointed out, it cancelled/disabled/borked other UI events (in my case touchUpInside wasn't firing on buttons within the UIScrollView's content view).

Using Juri's approach the buttons once again received touchUps whilst I could also scroll outside of the UIScrollView window. TouchUpInside events aren't captured by buttons outside of the UIScrollView, however, but for now this will do me.

Once again thanks!

A: 

Hi All, I too am working on the same feature. Although it works, its ugly. I got this link and seems like there is a different solution that works perfect. Can anyone please post in a sample code giving the basic idea for the solution.

Shrunga