自由分页的scrollView

今天看了人家的代码,自己写了一个可以自由分页的scrollView

再也不用局限于系统了

可以让他半屏分一页

效果图

如图如图

可以一平中显示三个页面

下边就直接上代码了

有很详细的注释我后边会把代码传到网上没具体的带时候你可以运行一下啊Demo当然我也是初学者水平可能不是很高有问题希望大家提出来

//
//  XXBPageScrollView.h
//  photoShowBrown
//
//  Created by Jinhong on 15-1-4.
//  Copyright (c) 2015年 xiaoxiaobing. All rights reserved.
//  可以自己控制分页的高度和宽度的scrollView

#import <UIKit/UIKit.h>

@interface XXBPageScrollView : UIScrollView <UIScrollViewDelegate>

/**
 *  用来设置分页的宽度,不设置的默认是屏幕的宽度
 */
@property (nonatomic) CGFloat pageWidth;

/**
 *  用来设置分页的高度,不设置墨粉是屏幕的高度
 */
@property (nonatomic) CGFloat pageHeight;

@end


//
//  XXBPageScrollView.m
//  photoShowBrown
//
//  Created by Jinhong on 15-1-4.
//  Copyright (c) 2015年 xiaoxiaobing. All rights reserved.
//

#import "XXBPageScrollView.h"
#import <objc/runtime.h>


/**
 *  默认快速清扫 20 就会更换一页
 */
#define DRAG_DISPLACEMENT_THRESHOLD 20

@interface XXBPageScrollView ()
{
    BOOL _delegateRespondsToWillBeginDragging;
    BOOL _delegateRespondsToWillEndDragging;
    BOOL _delegateRespondsToDidEndDragging;
    BOOL _delegateRespondsToDidEndDecelerating;
    BOOL _delegateRespondsToDidEndScrollingAnimation;
    BOOL _delegateRespondsToDidEndZooming;
    // 是否有动画
    BOOL _snapping;
    // 阻力特性
    CGPoint _dragVelocity;
    CGPoint _dragDisplacement;
}
@end
@implementation XXBPageScrollView

#pragma mark - 初始化控件
#pragma mark - Initialization

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    
    if (self)
    {
        [self performInit];
    }
    return self;
}

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    if (self) {
        [self performInit];
    }
    
    return self;
}

- (void)performInit {
    [super setDelegate:self];
    
    /**
     *  不管有没有分页默认的都设置成分页效果
     */
    if ([super isPagingEnabled]) {
        [super setPagingEnabled:NO];
        _pagingEnabled = YES;
    }
}

#pragma mark - 重写代理

@synthesize delegate = _actualDelegate;

- (void)setDelegate:(id<UIScrollViewDelegate>)delegate {
    if (delegate == _actualDelegate)
        return;
    _actualDelegate = delegate;
    
    // 把代理设置本控制器为了实现分页效果
    [super setDelegate:nil];
    [super setDelegate:self];
    
    // 自己存处一下代理
    _delegateRespondsToWillBeginDragging = [_actualDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)];
    _delegateRespondsToWillEndDragging = [_actualDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)];
    _delegateRespondsToDidEndDragging = [_actualDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
    _delegateRespondsToDidEndDecelerating = [_actualDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)];
    _delegateRespondsToDidEndScrollingAnimation = [_actualDelegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)];
    _delegateRespondsToDidEndZooming = [_actualDelegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)];
}
/**
 *   转发消息
 *
 *  @param 当给XXBPageScrollView设置代理的时候 本View 和 代理共同处理
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if (_actualDelegate && IsSelectorPartOfScrollViewDelegate([anInvocation selector])) {
        [anInvocation invokeWithTarget:_actualDelegate];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

/**
 *  重写父类的查找方法代理的方法是否存在的方法
 *
 *  @param aSelector 方法
 *
 *  @return 是否存在
 */
- (BOOL)respondsToSelector:(SEL)aSelector {
    BOOL respondsToSelector = [super respondsToSelector:aSelector];
    
    if (!respondsToSelector) {
        if (_actualDelegate && IsSelectorPartOfScrollViewDelegate(aSelector))
            respondsToSelector = [_actualDelegate respondsToSelector:aSelector];
    }
    
    return respondsToSelector;
}
/**
 *  利用运行时更改一些属性
 */
static inline BOOL IsSelectorPartOfScrollViewDelegate(SEL aSelector) {
    
    // 获取协议中指定条件的方法的方法描述数组
    // @optional 的方法
    struct objc_method_description optionalMethodDescription = protocol_getMethodDescription(@protocol(UIScrollViewDelegate), aSelector, NO, YES);
    // 必须实现的方法
    struct objc_method_description requiredMethodDescription = protocol_getMethodDescription(@protocol(UIScrollViewDelegate), aSelector, YES, YES);
    
    return optionalMethodDescription.name != NULL || requiredMethodDescription.name != NULL;
}

#pragma mark - 有关分页的设置

@synthesize pagingEnabled = _pagingEnabled;

- (void)setPagingEnabled:(BOOL)pagingEnabled {
    if (pagingEnabled == _pagingEnabled)
        return;
    
    
    _pagingEnabled = pagingEnabled;
    
    
    if (_pagingEnabled)
        [self snapToPage];
}

- (void)setPageWidth:(CGFloat)pageWidth {
    if (pageWidth == _pageWidth)
        return;
    
    _pageWidth = pageWidth;
    
    if (_pagingEnabled)
        [self snapToPage];
}

- (void)setPageHeight:(CGFloat)pageHeight {
    if (pageHeight == _pageHeight)
        return;
    
    
    _pageHeight = pageHeight;
    
    
    if (_pagingEnabled)
        [self snapToPage];
}

#pragma mark - 分页的相关算法

- (void)snapToPage {
    CGPoint pageOffset;
    pageOffset.x = [self pageOffsetForComponent:YES];
    pageOffset.y = [self pageOffsetForComponent:NO];
    
    
    CGPoint currentOffset = self.contentOffset;
    
    if (!CGPointEqualToPoint(pageOffset, currentOffset)) {
        _snapping = YES;
        
        [self setContentOffset:pageOffset animated:YES];
    }
    
    /**
     *  初始化为(0.0)
     */
    _dragVelocity = CGPointZero;
    _dragDisplacement = CGPointZero;
}

/**
 *  根据当前的位置计算出应该在的位置
 */
- (CGFloat)pageOffsetForComponent:(BOOL)isX {
    
    
    if (((isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds)) == 0) || ((isX ? self.contentSize.width : self.contentSize.height) == 0))
        return 0;
    
    
    CGFloat pageLength = isX ? _pageWidth : _pageHeight;
    
    if (pageLength < FLT_EPSILON)
        pageLength = isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds);
    
    pageLength *= self.zoomScale;
    
    
    CGFloat totalLength = isX ? self.contentSize.width : self.contentSize.height;
    
    CGFloat visibleLength = (isX ? CGRectGetWidth(self.bounds) : CGRectGetHeight(self.bounds)) * self.zoomScale;
    
    CGFloat currentOffset = isX ? self.contentOffset.x : self.contentOffset.y;
    
    CGFloat dragVelocity = isX ? _dragVelocity.x : _dragVelocity.y;
    
    CGFloat dragDisplacement = isX ? _dragDisplacement.x : _dragDisplacement.y;
    
    
    CGFloat newOffset;
    
    
    CGFloat index = currentOffset / pageLength;
    
    CGFloat lowerIndex = floorf(index);
    CGFloat upperIndex = ceilf(index);
    /**
     *  如果距离大于20 的话直接就分页了
     */
    if (ABS(dragDisplacement) < DRAG_DISPLACEMENT_THRESHOLD || dragDisplacement * dragVelocity < 0)
    {
        if (index - lowerIndex > upperIndex - index)
        {
            index = upperIndex;
        } else {
            index = lowerIndex;
        }
    }
    else
    {
        /**
         *  有了快速滑动的效果
         */
        if (dragVelocity > 0) {
            index = upperIndex;
        }
        else
        {
            index = lowerIndex;
        }
    }
    /**
     *  重新计算scrollView的offset
     */
    newOffset = pageLength * index;
    /**
     *  最后一个View的frame
     */
    if (newOffset > totalLength - visibleLength)
        newOffset = totalLength - visibleLength;
    
    /**
     *   第一个View的frame
     */
    if (newOffset < 0)
        newOffset = 0;
    
    return newOffset;
}

#pragma mark - scrollView的代理方法
/**
 *  写代理方法在自己处理的时候同时传给自己的代理
 *  这样就能共同处理事件 为了方便后续的相关处理
 */

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    _dragDisplacement = scrollView.contentOffset;
    if (_delegateRespondsToWillBeginDragging)
        [_actualDelegate scrollViewWillBeginDragging:scrollView];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    /**
     *  分页效果
     */
    if (_pagingEnabled)
    {
        *targetContentOffset = scrollView.contentOffset;
        _dragVelocity = velocity;
        _dragDisplacement = CGPointMake(scrollView.contentOffset.x - _dragDisplacement.x, scrollView.contentOffset.y - _dragDisplacement.y);
    }
    
    
    if (!_pagingEnabled && _delegateRespondsToWillEndDragging)
    {
        [_actualDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    if (!decelerate && _pagingEnabled)
        [self snapToPage];
    if (_delegateRespondsToDidEndDragging)
        [_actualDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (_pagingEnabled)
        [self snapToPage];
    if (_delegateRespondsToDidEndDecelerating)
        [_actualDelegate scrollViewDidEndDecelerating:scrollView];
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    if (!_snapping && _pagingEnabled)
    {
        [self snapToPage];
    }
    else
    {
        _snapping = NO;
    }
    
    
    if (_delegateRespondsToDidEndScrollingAnimation)
        [_actualDelegate scrollViewDidEndScrollingAnimation:scrollView];
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale{
    if (_pagingEnabled)
        [self snapToPage];
    if (_delegateRespondsToDidEndZooming)
        [_actualDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
}
@end


Demo: https://github.com/sixTiger/XXBAutoPagView

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值