UIScrollView的滑动视图切换(懒加载)

最近在做一个股票项目,项目中有这么一个需求,利用UIScrollView左右切换去展示新闻资讯的详情,而且资新闻资讯很多。用zaker看过新闻的都应该很熟悉这个场景了。我的第一反应就是UIScrollView的懒加载。具体思路就是整个UIScrollView中只加载三个视图,这里用左、中、右来区分。下面说说我的具体实现,以期抛砖引玉。

在进入详情之前,是新闻的标题展示页,用UITableView展示的。点击某个标题,就进入到了该条新闻的详情页了。注意了,你需要把该新闻是第几条的索引值pageIndex传过去,以便在UIScrollView进行切换新闻详情的时候,对其进行标识。我是通过UIViewController的init方法传过去的

- (instancetype)initWithWithPageIndex:(NSInteger)pageIndex withDatas:(NSArray *)newsList

其中,newList里面有很多新闻的model,用于数据请求传参。

这时候新闻详情页里面初始化了三条新闻,它们分别是第(pageIndex-1)条,第pageIndex条,第(pageIndex+1)条。当拖动UIScrollView向右滚动到下一页时,将pageIndex加1。同时,将原来的三条最左边展示新闻内容的视图干掉,中间和右边的视图则保留,还有就是把新的第(pageIndex+1)加载出来。拖动UIScrollView向右滚动查看新闻时就是这样。那么向左查看上一页新闻详情呢?与向右查看类似,当翻到上一页时,将pageIndex减1,同时,将原来的三条最右边展示新闻内容的视图干掉,保留左边和中间的视图,把新的第(pageIndex-1)条新闻的详情加载出来。核心思想就是这些,先看看我写的demo代码吧(代码中将新闻展示换成了图片展示)

@interface CycleViewController () <UIScrollViewDelegate>

@property (nonatomic, strong) UIScrollView *scrollView;

// 传过来的数据

@property (nonatomic, strong) NSArray *datas;

// 当前界面的索引 0开始,最大值为传过来的资源数量减1

@property (nonatomic, assign) NSInteger pageIndex;

// 用于存放当前界面中的三个元素

@property (nonatomic, strong) NSMutableArray *tempDatas;

// 当前视图的最右边的偏移量,用以判断当前视图是否离开了屏幕(查看下一页时)

@property (nonatomic, assign) CGFloat leftOffsetX;

// 当前视图的最左边的偏移量,用以判断当前视图是否离开了屏幕(查看上一页时)

@property (nonatomic, assign) CGFloat rightOffsetX;

// startContentOffsetX和willEndContentOffsetX是用来判断向左还是向右的。这里,我将查看下一页定义为向右,反之向左

@property (nonatomic, assign) CGFloat startContentOffsetX;

 @property (nonatomic, assign) CGFloat willEndContentOffsetX;

 

@property (nonatomic, assign) BOOL isDragged;

 

@end

 

#define kScrollViewWidth   CGRectGetWidth(self.scrollView.frame)

#define kScrollViewHeight  CGRectGetHeight(self.scrollView.frame)

 

@implementation CycleViewController

- (instancetype)initWithWithPageIndex:(NSInteger)pageIndex

                            withDatas:(NSArray *)datas {

    

    self = [super init];

    if (self) {

        

        self.pageIndex = pageIndex;

        self.datas = datas;

        _isDragged = NO;

    }

    return self;

}

 

- (NSMutableArray *)tempDatas {

    if (!_tempDatas) {

        _tempDatas = [NSMutableArray arrayWithCapacity:3];

    }

    return _tempDatas;

}

 

- (void)loadView {

    

    [super loadView];

    self.scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];

    self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

    self.scrollView.showsVerticalScrollIndicator = NO;

    self.scrollView.showsHorizontalScrollIndicator = NO;

    self.scrollView.delegate = self;

    self.scrollView.directionalLockEnabled = YES;

    self.scrollView.pagingEnabled = YES;

    [self.view addSubview:self.scrollView];

}

 

 

- (void)viewDidLoad {

    [super viewDidLoad];

    

    self.view.backgroundColor = [UIColor whiteColor];

    

    // 设置contentsizeheight0,意为scrollView的竖直方向不能拖动,解决scrollView的嵌套问题

    self.scrollView.contentSize = CGSizeMake(kScrollViewWidth*self.datas.count, 0);

    

    // 根据传过来的pageIndex进行初始化

    [self configImages];

    // 根据传过来的page索引值去初始化scrollView的偏移量

    [self.scrollView setContentOffset:CGPointMake(kScrollViewWidth*self.pageIndex, 0)];

}

 

 

- (UIImageView *)configImageViewWithImage:(UIImage *)image withFrame:(CGRect)frame {

    

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];

    imageView.contentMode = UIViewContentModeScaleAspectFit;

    imageView.image = image;

    return imageView;

}

 

- (void)configImages {

    

    UIImageView *firstImageView = nil;

    UIImageView *secondImageView = nil;

    UIImageView *thirdImageView = nil;

    

    // 当图片的数量小于三个情况 UIScrollView把图片都加载出来展示

    if (self.datas.count <= 3) {

        firstImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas firstObject]] withFrame:CGRectMake(0, 0, kScrollViewWidth, kScrollViewHeight)];

        [self.scrollView addSubview:firstImageView];

        

        if (self.datas.count >= 2) {

            secondImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:1]] withFrame:CGRectMake(kScrollViewWidth, 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:secondImageView];

            

            if (self.datas.count >= 3) {

                thirdImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:2]] withFrame:CGRectMake(kScrollViewWidth*2, 0, kScrollViewWidth, kScrollViewHeight)];

                [self.scrollView addSubview:thirdImageView];

            }

        }

    }

    // 当图片数量大于三个的情况下 根据索引值 初始化三个视图,然后放在scrollView上面

    else {

                

        if (self.pageIndex < 2) { // 当索引值小于2的情况,初始化所有图片的前三个

            

            secondImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:1]] withFrame:CGRectMake(kScrollViewWidth, 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:secondImageView];

            

            firstImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas firstObject]] withFrame:CGRectMake(0, 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:firstImageView];

            

            thirdImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:2]] withFrame:CGRectMake(kScrollViewWidth*2, 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:thirdImageView];

 

        }

        else if (self.pageIndex > self.datas.count-3) { // 初始化所有新源的最后三个

            

            secondImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.datas.count-2]] withFrame:CGRectMake(kScrollViewWidth*(self.datas.count-2), 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:secondImageView];

            

            firstImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.datas.count-3]] withFrame:CGRectMake(kScrollViewWidth*(self.datas.count-3), 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:firstImageView];

            

 

            

            thirdImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.datas.count-1]] withFrame:CGRectMake(kScrollViewWidth*(self.datas.count-1), 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:thirdImageView];

            

        }

        else { // 初始化索引值及其所有的图片内容

            

            secondImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.pageIndex]] withFrame:CGRectMake(kScrollViewWidth*self.pageIndex, 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:secondImageView];

   

            firstImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.pageIndex-1]] withFrame:CGRectMake(kScrollViewWidth*(self.pageIndex-1), 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:firstImageView];

            

            thirdImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.pageIndex+1]] withFrame:CGRectMake(kScrollViewWidth*(self.pageIndex+1), 0, kScrollViewWidth, kScrollViewHeight)];

            [self.scrollView addSubview:thirdImageView];

        }

    }

    // 这里添加的顺序要注意,不是随便添加的

    [self.tempDatas addObject:firstImageView];

    if (secondImageView != nil) {

        [self.tempDatas addObject:secondImageView];

    }

    if (thirdImageView != nil) {

        [self.tempDatas addObject:thirdImageView];

    }

    

    self.leftOffsetX = kScrollViewWidth*(self.pageIndex+1);

    self.rightOffsetX = kScrollViewWidth*self.pageIndex;

    

    // 刚开始点击了第几张

    self.title = [NSString stringWithFormat:@" %ld ", self.pageIndex+1];

}

 

 

#pragma mark == UIScrollViewDelegate ==

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    

    self.isDragged = YES;

    // 拖拽图片的起始偏移量

    self.startContentOffsetX = scrollView.contentOffset.x;

}

 

 

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    // 将要停止拖拽时 scrollView偏移的位置

    self.willEndContentOffsetX = scrollView.contentOffset.x;

}

 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {

    

    if (self.isDragged) {

        

        BOOL directionRight;

        CGFloat currentOffsetX = scrollView.contentOffset.x;

        if (currentOffsetX > self.willEndContentOffsetX && self.willEndContentOffsetX > self.startContentOffsetX) {

            directionRight = YES; // scrollView向右偏移

            // 当前是第几张

            int currentPage = ceil(scrollView.contentOffset.x/CGRectGetWidth(scrollView.frame))+1;

            if (currentPage <= self.datas.count) {

                self.title = [NSString stringWithFormat:@" %d ", currentPage];

            }

        }

        else if (currentOffsetX < self.willEndContentOffsetX && self.willEndContentOffsetX < self.startContentOffsetX) {

            directionRight = NO// scrollView向左偏移

            // 当前是第几张

            int currentPage = ceil(scrollView.contentOffset.x/CGRectGetWidth(scrollView.frame))+1;

            if (currentPage > 0) {

                self.title = [NSString stringWithFormat:@" %d ", currentPage];

            }

        }

        else {

            

            return; //  需要严格的条件判断 当滑动的幅度较小时,则不进行股票的预加载

        }

        

        if (directionRight) {

            

            if (self.leftOffsetX <= scrollView.contentOffset.x) { // 当前视图已经从界面中移除出去

                

                self.pageIndex++;

                

                if (self.pageIndex >= self.datas.count-1) {

                    self.pageIndex = self.datas.count-1;

                    self.leftOffsetX = CGRectGetWidth(scrollView.frame)*self.datas.count;

                    self.rightOffsetX = CGRectGetWidth(scrollView.frame)*(self.datas.count-1);

                    return;

                }

                

                self.leftOffsetX = CGRectGetWidth(scrollView.frame)*self.pageIndex;

                self.rightOffsetX = CGRectGetWidth(scrollView.frame)*(self.pageIndex-1);

            }

        }

        else {

            

            if (self.rightOffsetX >= scrollView.contentOffset.x) { // 当前视图已经从界面中移除出去

                

                self.pageIndex--;

                

                if (self.pageIndex <= 0) {

                    

                    self.pageIndex = 0;

                    self.leftOffsetX = CGRectGetWidth(scrollView.frame);

                    self.rightOffsetX = 0;

                    return;

                }

                

                self.leftOffsetX = CGRectGetWidth(scrollView.frame)*self.pageIndex;

                self.rightOffsetX = CGRectGetWidth(scrollView.frame)*(self.pageIndex-1);

            }

        }

    }

}

 

// 这个方法里面 才真正进行视图内容的加载

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

    

    int currentPage = ceil(scrollView.contentOffset.x/CGRectGetWidth(scrollView.frame))+1;

    self.title = [NSString stringWithFormat:@" %d ", currentPage];

    

    if (self.isDragged) {

        

        // 此处为容错判断 若滑动过快 将会导致currentPageself.pageIndex不一致 此时 scrollView上原来的视图删除掉,然后再进行重新布局

        if (self.datas.count > 3) {

            

            NSInteger currentPage = ceil(scrollView.contentOffset.x/CGRectGetWidth(scrollView.frame));

            if (currentPage != self.pageIndex) {

                self.pageIndex = currentPage;

                

                if (self.pageIndex >= self.datas.count-1) {

                    self.pageIndex = self.datas.count-1;

                }

                

                if (self.pageIndex <= 0) {

                    self.pageIndex = 0;

                }

                

                for (UIImageView *imageView in self.tempDatas) {

                    [imageView removeFromSuperview];

                }

                

                [self.tempDatas removeAllObjects];

                

                [self configImages];

                return;

            }

        }        

        

        BOOL directionRight;

        CGFloat endOffsetX = scrollView.contentOffset.x;

        if (endOffsetX > self.willEndContentOffsetX && self.willEndContentOffsetX > self.startContentOffsetX) {

            directionRight = YES;

        }

        else if (endOffsetX < self.willEndContentOffsetX && self.willEndContentOffsetX < self.startContentOffsetX) {

            directionRight = NO;

        }

        else {

            

            // 容错检查 当滑动到左边或右边的的边界时,若显示了空白页,刚重新进行布局,加载最左边或最右边的三支股票

            if (self.datas.count > 3) { // 在资源的数量小于3个的情况因全部加载了内容,所以不需要这种容错检查(下同)

                UIImageView *imageView = nil;

                if (scrollView.contentOffset.x <= 0) {

                    imageView = [self.tempDatas objectAtIndex:0]; // 最左边

                }

                else if (scrollView.contentOffset.x+CGRectGetWidth(scrollView.frame) >= CGRectGetWidth(scrollView.frame)*self.datas.count) { // 最右边

                    imageView = [self.tempDatas objectAtIndex:2];

                }

                else {

                    imageView = [self.tempDatas objectAtIndex:1];

                }

                

                if (imageView.frame.origin.x != CGRectGetWidth(scrollView.frame)*self.pageIndex) {

                    

                    for (UIImageView *imageView in self.tempDatas) {

                        [imageView removeFromSuperview];

                    }

                    [self.tempDatas removeAllObjects];

                    [self configImages];

                }

            }

            

            return; //  需要严格的条件判断 当滑动的幅度较小时,则不进行股票的预加载

        }

        // 容错检查 若滑动较快,停在了中间某个位置时,若显示空白,则替换掉self.tempDatas中旧的视图控制器,重新进行布局

        if (self.datas.count > 3) {

            UIImageView *imageView = nil;

            if (directionRight) {

                if (self.pageIndex == 1) { // 向右滑动, self.pageIndex1的情况下进行检查

                    imageView = [self.tempDatas objectAtIndex:1];

                }

                else {

                    imageView = [self.tempDatas objectAtIndex:2];

                }

            }

            else {

                if (self.pageIndex == self.datas.count - 2) { // 向左滑动,self.pageIndexself.dataList.count-2的情况下进行容错检查

                    imageView = [self.tempDatas objectAtIndex:1];

                }

                else {

                    imageView = [self.tempDatas objectAtIndex:0];

                }

            }

            

            if (imageView.frame.origin.x != CGRectGetWidth(scrollView.frame)*self.pageIndex) {

                for (UIImageView *imageView in self.tempDatas) {

                    [imageView removeFromSuperview];

                }

                [self.tempDatas removeAllObjects];

                [self configImages];

            }

        }

        

          if (directionRight) {

            

            // 这里 leftOffsetX还原,以不影响视图的加载

            self.leftOffsetX -= CGRectGetWidth(scrollView.frame);

            self.rightOffsetX -= CGRectGetWidth(scrollView.frame);

            if (self.leftOffsetX <= scrollView.contentOffset.x) {

                if (self.pageIndex >= self.datas.count-1) {

                    self.pageIndex = self.datas.count-1;

                    UIImageView *rightImageView = [self.tempDatas lastObject];

                    self.leftOffsetX = rightImageView.frame.origin.x+CGRectGetWidth(rightImageView.frame);

                    self.rightOffsetX = rightImageView.frame.origin.x;

                    return;

                }

                

                if (self.pageIndex == 1) { // 翻到最左边 然后回翻时 此种情况数组中的三个VC不动

                    

                    UIImageView *middleImageView = [self.tempDatas objectAtIndex:1];

                    self.leftOffsetX = middleImageView.frame.origin.x + CGRectGetWidth(middleImageView.frame);

                    self.rightOffsetX = middleImageView.frame.origin.x;

                    return;

                }

                

                UIImageView *firstImageView = [self.tempDatas firstObject];

                [firstImageView removeFromSuperview];

                

                UIImageView *thirdImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.pageIndex+1]] withFrame:CGRectMake(kScrollViewWidth*(self.pageIndex+1), 0, kScrollViewWidth, kScrollViewHeight)];

                [self.scrollView addSubview:thirdImageView];

                [self.tempDatas addObject:thirdImageView];

                [self.tempDatas removeObjectAtIndex:0];

                

                UIImageView *secondImageView = [self.tempDatas objectAtIndex:1];

                self.leftOffsetX = secondImageView.frame.origin.x + CGRectGetWidth(secondImageView.frame);

                self.rightOffsetX = secondImageView.frame.origin.x;

            }

        }

        else {

            

            // 这里 leftOffsetX还原 以正常加载视图

            self.leftOffsetX += CGRectGetWidth(scrollView.frame);

            self.rightOffsetX += CGRectGetWidth(scrollView.frame);

            

            if (self.rightOffsetX >= scrollView.contentOffset.x) {

                if (self.pageIndex <= 0) {

                  

                    self.pageIndex = 0;

                    UIImageView *firstImageView = [self.tempDatas firstObject];

                    self.leftOffsetX = firstImageView.frame.origin.x+CGRectGetWidth(firstImageView.frame);

                    self.rightOffsetX = firstImageView.frame.origin.x;

                    return;

                }

                

                if (self.pageIndex == self.datas.count - 2) { // 翻到最右边 回翻 翻到倒数第二个的时候 数组中的三个VC保持不变

                    

                    UIImageView *secondImageView = [self.tempDatas objectAtIndex:1];

                    self.leftOffsetX = secondImageView.frame.origin.x + CGRectGetWidth(secondImageView.frame);

                    self.rightOffsetX = secondImageView.frame.origin.x;

                    return;

                }

                

                UIImageView *rightImageView = [self.tempDatas lastObject];

                [rightImageView removeFromSuperview];

                

                UIImageView *firstImageView = [self configImageViewWithImage:[UIImage imageNamed:[self.datas objectAtIndex:self.pageIndex-1]] withFrame:CGRectMake(kScrollViewWidth*(self.pageIndex-1), 0, kScrollViewWidth, kScrollViewHeight)];

                [self.scrollView addSubview:firstImageView];

                [self.tempDatas removeLastObject];

                [self.tempDatas insertObject:firstImageView atIndex:0];

                

                UIImageView *secondImageView = [self.tempDatas objectAtIndex:1];

                self.leftOffsetX = secondImageView.frame.origin.x + CGRectGetWidth(secondImageView.frame);

                self.rightOffsetX = secondImageView.frame.origin.x;

            }

        }

    }

}

晕了吗?如果没晕,说明你是一个能hold得住的老程序员了。所谓台上十分钟,台下十年功。程序员就是一个修炼的过程。

不过怎么会有那么一大坨的代码?别急,容我再叙。

一开始,我出没写那么多,然后写出来之后在真机上我也愉快的体验了一下,挺好,测试测了一下,也挺好。你好我好大家好,然后我就玩去了。话说要提交新版本前的那个晚上,一切都那么和谐,只等打包上传然后回家过周末了!好happy啊!

但是,测试的手一抖,然后,就加班到了凌晨后,容我做一个悲伤的表情!

事情是这样的,他左右拖动新闻的速度比较快,然后,界面就乱了!当时我就郁闷了,为什么?一看,因为我将视图的加载放到了UIScrollView的

- (void)scrollViewDidScroll:(UIScrollView *)scrollView方法中了,当左右拖动较快时,屏幕就乱了。

这个时候,我就想了,在你左右拖动较快的情况下(很可能是无聊瞎划拉),就去给你加载,而你又不看,不是太傻了吗!那好吧,就懒到底吧,把加载放到scrollView停下来的代理方法里面

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

还有,就是要处理数组越界的情况,容错处理,上面代码里面我注释了。

初来乍到,写的比较乱,望多多指教!

转载于:https://www.cnblogs.com/zhangguangkai/p/5300079.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值