UIScrollView horizontal paging like Mobile Safari

2019-01-01 05:59发布

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?

9条回答
有味是清欢
2楼-- · 2019-01-01 06:36

I have made another implementation which can return the scrollview automatically. So it don't need to have an IBOutlet which will limit reusage in project.

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    if ([self pointInside:point withEvent:event]) {
        for (id view in [self subviews]) {
            if ([view isKindOfClass:[UIScrollView class]]) {
                return view;
            }
        }
    }
    return nil;
}
查看更多
心情的温度
3楼-- · 2019-01-01 06:36

Enable firing tap events on child views of the scroll view while supporting the technique of this SO question. Uses a reference to the scroll view (self.scrollView) for readability.

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *hitView = nil;
    NSArray *childViews = [self.scrollView subviews];
    for (NSUInteger i = 0; i < childViews.count; i++) {
        CGRect childFrame = [[childViews objectAtIndex:i] frame];
        CGRect scrollFrame = self.scrollView.frame;
        CGPoint contentOffset = self.scrollView.contentOffset;
        if (childFrame.origin.x + scrollFrame.origin.x < point.x + contentOffset.x &&
            point.x + contentOffset.x < childFrame.origin.x + scrollFrame.origin.x + childFrame.size.width &&
            childFrame.origin.y + scrollFrame.origin.y < point.y + contentOffset.y &&
            point.y + contentOffset.y < childFrame.origin.y + scrollFrame.origin.y + childFrame.size.height
        ){
            hitView = [childViews objectAtIndex:i];
            return hitView;
        }
    }
    hitView = [super hitTest:point withEvent:event];
    if (hitView == self)
        return self.scrollView;
    return hitView;
}

Add this to your child view to capture the touch event:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

(This is a variation on user1856273's solution. Cleaned up for readability and incorporated Bartserk's bug fix. I thought of editing user1856273's answer but it was too big a change to make.)

查看更多
一个人的天荒地老
4楼-- · 2019-01-01 06:38

I wound up going with the custom UIScrollView myself as it was the quickest and simpler method it seemed to me. However, I didn't see any exact code so figured I would share. My needs were for a UIScrollView that had small content and therefore the UIScrollView itself was small to achieve the paging affect. As the post states you can't swipe across. But now you can.

Create a class CustomScrollView and subclass UIScrollView. Then all you need to do is add this into the .m file:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
  return (point.y >= 0 && point.y <= self.frame.size.height);
}

This allows you to scroll from side to side (horizontal). Adjust the bounds accordingly to set your swipe/scrolling touch area. Enjoy!

查看更多
怪性笑人.
5楼-- · 2019-01-01 06:39

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 {
  return [self pointInside:point withEvent:event] ? scrollView : 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.

NOTE: You should also take a look at Juri Pakaste's hitTest:withEvent: modification if you are having issues with subview user interaction.

查看更多
泛滥B
6楼-- · 2019-01-01 06:39

my version Pressing the button lying on the scroll - work =)

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView* child = nil;
    for (int i=0; i<[[[[self subviews] objectAtIndex:0] subviews] count];i++) {

        CGRect myframe =[[[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i] frame];
        CGRect myframeS =[[[self subviews] objectAtIndex:0] frame];
        CGPoint myscroll =[[[self subviews] objectAtIndex:0] contentOffset];
        if (myframe.origin.x < point.x && point.x < myframe.origin.x+myframe.size.width &&
            myframe.origin.y+myframeS.origin.y < point.y+myscroll.y && point.y+myscroll.y < myframe.origin.y+myframeS.origin.y +myframe.size.height){
            child = [[[[self subviews] objectAtIndex:0] subviews]objectAtIndex:i];
            return child;
        }


    }

    child = [super hitTest:point withEvent:event];
    if (child == self)
        return [[self subviews] objectAtIndex:0];
    return child;
    }

but only [[self subviews] objectAtIndex: 0] must be a scroll

查看更多
刘海飞了
7楼-- · 2019-01-01 06:41

Set frame size of scrollView as your pages size would be:

[self.view addSubview:scrollView];
[self.view addGestureRecognizer:mainScrollView.panGestureRecognizer];

Now you can pan on self.view, and content on scrollView will be scrolled.
Also use scrollView.clipsToBounds = NO; to prevent clipping the content.

查看更多
登录 后发表回答