用AutoLayout實現分頁滾動

歐陽大哥2013 2019-06-26 10:18:17 2105

作者:歐陽大哥2013

滾動視圖分頁

UIScrollView的pagingEnabled屬性用于控制是否按分頁進行滾動。在一些應用中會應用到這一個特性,最典型的就是手機桌面的應用圖標列表。這些界面中往往每一頁功能都比較獨立,系統也提供了UIPageViewController來實現這種分頁滾動的功能。

實現分頁滾動的UI實現一般是最外層一個UIScrollView。然后UIScrollView里面是一個總體的容器視圖containerView。容器視圖添加N個頁視圖,對于水平分頁滾動來說容器視圖的高度和滾動視圖一樣,而寬度則是滾動視圖的寬度乘以頁視圖的數量,頁視圖的尺寸則和滾動視圖保持一致,對于垂直分頁滾動來說容器視圖的寬度和滾動視圖一樣,而高度則是滾動視圖的高度乘以頁視圖的數量,頁視圖的尺寸則和滾動視圖保持一致。每個頁視圖中在添加各自的條目視圖。整體效果圖如下:

image.png

AutoLayout實現分頁滾動的方法

根據上面的UI結構這里用AutoLayout的代碼來實現水平分頁的滾動。這里的約束設置代碼是iOS9以后提供的相關API。

- (void)loadView {
    
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    if (@available(iOS 11.0, *)) {
        scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        // Fallback on earlier versions
    }
    scrollView.pagingEnabled = YES;
    scrollView.backgroundColor = [UIColor whiteColor];
    self.view = scrollView;
    
    //建立容器視圖
    UIView *containerView = [UIView new];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    [scrollView addSubview:containerView];
    
    //設置容器的四個邊界和滾動視圖保持一致的約束。
    [containerView.leftAnchor constraintEqualToAnchor:scrollView.leftAnchor].active = YES;
    [containerView.topAnchor constraintEqualToAnchor:scrollView.topAnchor].active = YES;
    [containerView.rightAnchor constraintEqualToAnchor:scrollView.rightAnchor].active = YES;
    [containerView.bottomAnchor constraintEqualToAnchor:scrollView.bottomAnchor].active = YES;
    //容器視圖的高度和滾動視圖保持一致。
    [containerView.heightAnchor constraintEqualToAnchor:scrollView.heightAnchor].active = YES;
    //添加頁視圖
    NSArray *colors = @[[UIColor redColor],[UIColor greenColor], [UIColor blueColor]];
    NSMutableArray *pageViews = [NSMutableArray arrayWithCapacity:colors.count];
    NSLayoutXAxisAnchor *prevLeftAnchor = containerView.leftAnchor;
    for (int i = 0; i < colors.count; i++)
    {
        //建立頁視圖
        UIView *pageView = [UIView new];
        pageView.backgroundColor = colors[i];
        pageView.translatesAutoresizingMaskIntoConstraints = NO;
        [containerView addSubview:pageView];
        
        //頁視圖分別從左往右排列,第1頁的左邊約束是容器視圖的左邊,其他頁的左邊約束則是前面兄弟視圖的右邊。
        [pageView.leftAnchor constraintEqualToAnchor:prevLeftAnchor].active = YES;
        //每頁的頂部約束是容器視圖。
        [pageView.topAnchor constraintEqualToAnchor:containerView.topAnchor].active = YES;
        //每頁的寬度約束是滾動視圖
        [pageView.widthAnchor constraintEqualToAnchor:scrollView.widthAnchor].active = YES;
        //每頁的高度約束是滾動視圖
        [pageView.heightAnchor constraintEqualToAnchor:scrollView.heightAnchor].active = YES;
        
        prevLeftAnchor = pageView.rightAnchor;
        
        [pageViews addObject:pageView];
        
    }
    
    //關鍵的一步,如果需要左右滾動則將容器視圖中的最右部子視圖這里是B的右邊邊界依賴于容器視圖的右邊邊界。
    [pageViews.lastObject.rightAnchor constraintEqualToAnchor:containerView.rightAnchor].active = YES;
    
    //這里可以為每個頁視圖添加不同的條目視圖,具體實現大家自行添加代碼吧。
}

下面是運行時的效果圖:

MyLayout實現分頁滾動的方法

你也可以用MyLayout布局庫來實現分頁滾動的能力。MyLayout布局庫是筆者開源的一套功能強大的UI布局庫。

您可以從github地址: github.com/youngsoft/M… 下載或者從podfile中導入:

pod  'MyLayout'

復制代碼來使用MyLayout。下面是具體用MyLayout來實現分頁滾動的代碼。

//
#import - (void)loadView {
    
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    if (@available(iOS 11.0, *)) {
        scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        // Fallback on earlier versions
    }
    scrollView.pagingEnabled = YES;
    scrollView.backgroundColor = [UIColor whiteColor];
    self.view = scrollView;
    
    
    //建立一個水平線性布局容器視圖
    MyLinearLayout *containerView = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz];
    containerView.myVertMargin = 0;  //水平線性布局的上下邊界和滾動視圖保持一致,這里也會確定線性布局的高度。
    containerView.gravity = MyGravity_Vert_Fill | MyGravity_Horz_Fill;  //設置線性布局中的所有子視圖均分和填充線性布局的高度和寬度。
    [scrollView addSubview:containerView];
    
    
    //添加頁視圖
    NSArray *colors = @[[UIColor redColor],[UIColor greenColor], [UIColor blueColor]];
    NSMutableArray *pageViews = [NSMutableArray arrayWithCapacity:colors.count];
    for (int i = 0; i < colors.count; i++)
    {
        //建立頁視圖
        UIView *pageView = [UIView new];
        pageView.backgroundColor = colors[i];
        [containerView addSubview:pageView];
        
        //因為線性布局通過屬性gravity的設置就可以確定子頁視圖的高度和寬度,再加上線性布局的特性,所以頁視圖不需要設置任何附加的約束。
        
        [pageViews addObject:pageView];
        
    }
    
    //關鍵的一步, 設置線性布局的寬度是滾動視圖的倍數
    containerView.widthSize.equalTo(scrollView.widthSize).multiply(colors.count);
    
    
    //這里可以為每個頁視圖添加不同的條目視圖,具體實現大家自行添加代碼吧。
}

MyLayout實現桌面的圖標列表分頁功能

MyLayout中的流式布局MyFlowLayout所具備的能力和flex-box相似,甚至有些特性要強于后者。流式布局用于一些子視圖有規律排列的場景,就比如本例子中的滾動分頁的圖標列表的能力。下面就是具體的實現代碼。

- (void)loadView {
    
    UIScrollView *scrollView = [[UIScrollView alloc] init];
    if (@available(iOS 11.0, *)) {
        scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
    } else {
        // Fallback on earlier versions
    }
    scrollView.pagingEnabled = YES;
    scrollView.backgroundColor = [UIColor whiteColor];
    self.view = scrollView;
    
    
    //建立一個垂直數量約束流式布局:每列展示3個子視圖,每頁展示9個子視圖,整體從左往右滾動。
    MyFlowLayout *containerView = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3];
    containerView.pagedCount = 9; //pagedCount設置為非0時表示開始分頁展示的功能,這里表示每頁展示9個子視圖,這個數量必須是arrangedCount的倍數。
    containerView.wrapContentWidth = YES; //設置布局視圖的寬度由子視圖包裹,當垂直流式布局的這個屬性設置為YES,并和pagedCount搭配使用會產生分頁從左到右滾動的效果。
    containerView.myVertMargin = 0; //容器視圖的高度和滾動視圖保持一致。
   
    containerView.subviewHSpace = 10;
    containerView.subviewVSpace = 10;  //設置子視圖的水平和垂直間距。
    containerView.padding = UIEdgeInsetsMake(5, 5, 5, 5); //布局視圖的內邊距設置。
    [scrollView addSubview:containerView];
    
    //建立條目視圖
    for (int i = 0; i < 40; i++)
    {
        UILabel *label = [UILabel new];
        label.textAlignment = NSTextAlignmentCenter;
        label.backgroundColor = [UIColor greenColor];
        label.text = [NSString stringWithFormat:@"%d",i];
        [containerView addSubview:label];
    }
    
    
    //獲取流式布局的橫屏size classes,并且設置設備處于橫屏時,每排數量由3個變為6個,每頁的數量由9個變為18個。
    MyFlowLayout *containerViewSC = [containerView fetchLayoutSizeClass:MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny];
    containerViewSC.arrangedCount = 6;
    containerViewSC.pagedCount = 18;

從上面的代碼可以看出要實現分頁滾動的圖標列表的能力,主要是對充當容器視圖的流式布局設置一些屬性即可,不需要為條目設置任何約束,而且還支持橫豎屏下每頁的不同數量的展示能力。整個功能代碼量少,對比用UICollectionView來實現相同的功能要簡潔和容易得多。下面是程序運行的效果:

橫豎屏切換

對于帶有分頁功能的滾動視圖來說,當需要支持橫豎屏時就有可能會出現橫豎屏切換時界面停留在兩個頁面中間而不是按頁進行滾動的效果。其原因是無論是分頁滾動還是不分頁滾動,在滾動時都是通過調整滾動視圖的contentOffset來實現的。而當滾動視圖進行橫豎屏切換時不會調整對應的contentOffset值,這樣就導致了在屏幕方向切換時的滾動位置出現異常。解決的辦法就是在屏幕滾動時的相應回調處理方法中修正這個contentOffset的值來解決這個問題。比如我們可以在屏幕切換的sizeclass變化的視圖控制器的協議方法中添加如下代碼:

- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection
{
    [super traitCollectionDidChange:previousTraitCollection];
    
    UIScrollView *scrollView = (UIScrollView*)self.view;
    
    //根據當前的contentOffset調整到正確的contentOffset
    int pageIndex = scrollView.contentOffset.x / scrollView.frame.size.width;
    int pages = scrollView.contentSize.width / scrollView.frame.size.width;
    if (pageIndex >= pages)
        pageIndex = pages - 1;
    if (pageIndex < 0)
        pageIndex = 0;
    
    scrollView.contentOffset = CGPointMake(pageIndex * scrollView.frame.size.width, scrollView.contentOffset.y);
    
}


广东26选5开奖结果查