自由分頁的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

發佈了51 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章