UIScrollView的滑動視圖切換(懶加載)

最近在作一個股票項目,項目中有這麼一個需求,利用UIScrollView左右切換去展現新聞資訊的詳情,並且資新聞資訊不少。用zaker看過新聞的都應該很熟悉這個場景了。個人第一反應就是UIScrollView的懶加載。具體思路就是整個UIScrollView中只加載三個視圖,這裏用左、中、右來區分。下面說說個人具體實現,以期拋磚引玉。程序員

在進入詳情以前,是新聞的標題展現頁,用UITableView展現的。點擊某個標題,就進入到了該條新聞的詳情頁了。注意了,你須要把該新聞是第幾條的索引值pageIndex傳過去,以便在UIScrollView進行切換新聞詳情的時候,對其進行標識。我是經過UIViewController的init方法傳過去的數組

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

其中,newList裏面有不少新聞的model,用於數據請求傳參。佈局

這時候新聞詳情頁裏面初始化了三條新聞,它們分別是第(pageIndex-1)條,第pageIndex條,第(pageIndex+1)條。當拖動UIScrollView向右滾動到下一頁時,將pageIndex加1。同時,將原來的三條最左邊展現新聞內容的視圖幹掉,中間和右邊的視圖則保留,還有就是把新的第(pageIndex+1)加載出來。拖動UIScrollView向右滾動查看新聞時就是這樣。那麼向左查看上一頁新聞詳情呢?與向右查看相似,當翻到上一頁時,將pageIndex減1,同時,將原來的三條最右邊展現新聞內容的視圖幹掉,保留左邊和中間的視圖,把新的第(pageIndex-1)條新聞的詳情加載出來。核心思想就是這些,先看看我寫的demo代碼吧(代碼中將新聞展現換成了圖片展現)測試

@interface CycleViewController () <UIScrollViewDelegate>atom

@property (nonatomic, strong) UIScrollView *scrollView;spa

// 傳過來的數據代理

@property (nonatomic, strong) NSArray *datas;orm

// 當前界面的索引 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

還有,就是要處理數組越界的狀況,容錯處理,上面代碼裏面我註釋了。

初來乍到,寫的比較亂,望多多指教!

相關文章
相關標籤/搜索