用AutoLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)

滾動(dòng)視圖分頁(yè)

UIScrollView的pagingEnabled屬性用于控制是否按分頁(yè)進(jìn)行滾動(dòng)胜嗓。在一些應(yīng)用中會(huì)應(yīng)用到這一個(gè)特性员帮,最典型的就是手機(jī)桌面的應(yīng)用圖標(biāo)列表惦蚊。這些界面中往往每一頁(yè)功能都比較獨(dú)立,系統(tǒng)也提供了UIPageViewController來實(shí)現(xiàn)這種分頁(yè)滾動(dòng)的功能躬窜。
實(shí)現(xiàn)分頁(yè)滾動(dòng)的UI實(shí)現(xiàn)一般是最外層一個(gè)UIScrollView浇垦。然后UIScrollView里面是一個(gè)總體的容器視圖containerView。容器視圖添加N個(gè)頁(yè)視圖荣挨,對(duì)于水平分頁(yè)滾動(dòng)來說容器視圖的高度和滾動(dòng)視圖一樣男韧,而寬度則是滾動(dòng)視圖的寬度乘以頁(yè)視圖的數(shù)量,頁(yè)視圖的尺寸則和滾動(dòng)視圖保持一致默垄,對(duì)于垂直分頁(yè)滾動(dòng)來說容器視圖的寬度和滾動(dòng)視圖一樣此虑,而高度則是滾動(dòng)視圖的高度乘以頁(yè)視圖的數(shù)量,頁(yè)視圖的尺寸則和滾動(dòng)視圖保持一致厕倍。每個(gè)頁(yè)視圖中在添加各自的條目視圖寡壮。整體效果圖如下:


分頁(yè)滾動(dòng)UI布局

AutoLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)的方法

根據(jù)上面的UI結(jié)構(gòu)這里用AutoLayout的代碼來實(shí)現(xiàn)水平分頁(yè)的滾動(dòng)贩疙。這里的約束設(shè)置代碼是iOS9以后提供的相關(guān)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];
    
    //設(shè)置容器的四個(gè)邊界和滾動(dòng)視圖保持一致的約束。
    [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;
    //容器視圖的高度和滾動(dòng)視圖保持一致这溅。
    [containerView.heightAnchor constraintEqualToAnchor:scrollView.heightAnchor].active = YES;

    //添加頁(yè)視圖
    NSArray<UIColor*> *colors = @[[UIColor redColor],[UIColor greenColor], [UIColor blueColor]];
    NSMutableArray<UIView*> *pageViews = [NSMutableArray arrayWithCapacity:colors.count];
    NSLayoutXAxisAnchor *prevLeftAnchor = containerView.leftAnchor;
    for (int i = 0; i < colors.count; i++)
    {
        //建立頁(yè)視圖
        UIView *pageView = [UIView new];
        pageView.backgroundColor = colors[i];
        pageView.translatesAutoresizingMaskIntoConstraints = NO;
        [containerView addSubview:pageView];
        
        //頁(yè)視圖分別從左往右排列组民,第1頁(yè)的左邊約束是容器視圖的左邊,其他頁(yè)的左邊約束則是前面兄弟視圖的右邊悲靴。
        [pageView.leftAnchor constraintEqualToAnchor:prevLeftAnchor].active = YES;
        //每頁(yè)的頂部約束是容器視圖臭胜。
        [pageView.topAnchor constraintEqualToAnchor:containerView.topAnchor].active = YES;
        //每頁(yè)的寬度約束是滾動(dòng)視圖
        [pageView.widthAnchor constraintEqualToAnchor:scrollView.widthAnchor].active = YES;
        //每頁(yè)的高度約束是滾動(dòng)視圖
        [pageView.heightAnchor constraintEqualToAnchor:scrollView.heightAnchor].active = YES;
        
        prevLeftAnchor = pageView.rightAnchor;
        
        [pageViews addObject:pageView];
        
    }
    
    //關(guān)鍵的一步,如果需要左右滾動(dòng)則將容器視圖中的最右部子視圖這里是B的右邊邊界依賴于容器視圖的右邊邊界。
    [pageViews.lastObject.rightAnchor constraintEqualToAnchor:containerView.rightAnchor].active = YES;
    
    //這里可以為每個(gè)頁(yè)視圖添加不同的條目視圖耸三,具體實(shí)現(xiàn)大家自行添加代碼吧乱陡。

}

下面是運(yùn)行時(shí)的效果圖:


分頁(yè)滾動(dòng)

MyLayout實(shí)現(xiàn)分頁(yè)滾動(dòng)的方法

你也可以用MyLayout布局庫(kù)來實(shí)現(xiàn)分頁(yè)滾動(dòng)的能力。MyLayout布局庫(kù)是筆者開源的一套功能強(qiáng)大的UI布局庫(kù)仪壮。
您可以從github地址: https://github.com/youngsoft/MyLinearLayout 下載或者從podfile中導(dǎo)入:

pod  'MyLayout'

來使用MyLayout憨颠。下面是具體用MyLayout來實(shí)現(xiàn)分頁(yè)滾動(dòng)的代碼。

//
#import <MyLayout.h>

- (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;
    
    
    //建立一個(gè)水平線性布局容器視圖
    MyLinearLayout *containerView = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz];
    containerView.myVertMargin = 0;  //水平線性布局的上下邊界和滾動(dòng)視圖保持一致积锅,這里也會(huì)確定線性布局的高度爽彤。
    containerView.gravity = MyGravity_Vert_Fill | MyGravity_Horz_Fill;  //設(shè)置線性布局中的所有子視圖均分和填充線性布局的高度和寬度。
    [scrollView addSubview:containerView];
    
    
    //添加頁(yè)視圖
    NSArray<UIColor*> *colors = @[[UIColor redColor],[UIColor greenColor], [UIColor blueColor]];
    NSMutableArray<UIView*> *pageViews = [NSMutableArray arrayWithCapacity:colors.count];
    for (int i = 0; i < colors.count; i++)
    {
        //建立頁(yè)視圖
        UIView *pageView = [UIView new];
        pageView.backgroundColor = colors[i];
        [containerView addSubview:pageView];
        
        //因?yàn)榫€性布局通過屬性gravity的設(shè)置就可以確定子頁(yè)視圖的高度和寬度缚陷,再加上線性布局的特性适篙,所以頁(yè)視圖不需要設(shè)置任何附加的約束。
        
        [pageViews addObject:pageView];
        
    }
    
    //關(guān)鍵的一步, 設(shè)置線性布局的寬度是滾動(dòng)視圖的倍數(shù)
    containerView.widthSize.equalTo(scrollView.widthSize).multiply(colors.count);
    
    
    //這里可以為每個(gè)頁(yè)視圖添加不同的條目視圖箫爷,具體實(shí)現(xiàn)大家自行添加代碼吧嚷节。

}

MyLayout實(shí)現(xiàn)桌面的圖標(biāo)列表分頁(yè)功能

MyLayout中的流式布局MyFlowLayout所具備的能力和flex-box相似,甚至有些特性要強(qiáng)于后者蝶缀。流式布局用于一些子視圖有規(guī)律排列的場(chǎng)景丹喻,就比如本例子中的滾動(dòng)分頁(yè)的圖標(biāo)列表的能力。下面就是具體的實(shí)現(xiàn)代碼翁都。

- (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;
    
    
    //建立一個(gè)垂直數(shù)量約束流式布局:每列展示3個(gè)子視圖,每頁(yè)展示9個(gè)子視圖碍论,整體從左往右滾動(dòng)。
    MyFlowLayout *containerView = [MyFlowLayout flowLayoutWithOrientation:MyOrientation_Vert arrangedCount:3];
    containerView.pagedCount = 9; //pagedCount設(shè)置為非0時(shí)表示開始分頁(yè)展示的功能柄慰,這里表示每頁(yè)展示9個(gè)子視圖鳍悠,這個(gè)數(shù)量必須是arrangedCount的倍數(shù)。
    containerView.wrapContentWidth = YES; //設(shè)置布局視圖的寬度由子視圖包裹坐搔,當(dāng)垂直流式布局的這個(gè)屬性設(shè)置為YES藏研,并和pagedCount搭配使用會(huì)產(chǎn)生分頁(yè)從左到右滾動(dòng)的效果。
    containerView.myVertMargin = 0; //容器視圖的高度和滾動(dòng)視圖保持一致概行。
   
    containerView.subviewHSpace = 10;
    containerView.subviewVSpace = 10;  //設(shè)置子視圖的水平和垂直間距蠢挡。
    containerView.padding = UIEdgeInsetsMake(5, 5, 5, 5); //布局視圖的內(nèi)邊距設(shè)置。
    [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凳忙,并且設(shè)置設(shè)備處于橫屏?xí)r,每排數(shù)量由3個(gè)變?yōu)?個(gè)业踏,每頁(yè)的數(shù)量由9個(gè)變?yōu)?8個(gè)。
    MyFlowLayout *containerViewSC = [containerView fetchLayoutSizeClass:MySizeClass_Landscape copyFrom:MySizeClass_wAny | MySizeClass_hAny];
    containerViewSC.arrangedCount = 6;
    containerViewSC.pagedCount = 18;

從上面的代碼可以看出要實(shí)現(xiàn)分頁(yè)滾動(dòng)的圖標(biāo)列表的能力涧卵,主要是對(duì)充當(dāng)容器視圖的流式布局設(shè)置一些屬性即可勤家,不需要為條目設(shè)置任何約束,而且還支持橫豎屏下每頁(yè)的不同數(shù)量的展示能力柳恐。整個(gè)功能代碼量少伐脖,對(duì)比用UICollectionView來實(shí)現(xiàn)相同的功能要簡(jiǎn)潔和容易得多热幔。下面是程序運(yùn)行的效果:


分頁(yè)圖標(biāo)效果圖

橫豎屏切換

對(duì)于帶有分頁(yè)功能的滾動(dòng)視圖來說,當(dāng)需要支持橫豎屏?xí)r就有可能會(huì)出現(xiàn)橫豎屏切換時(shí)界面停留在兩個(gè)頁(yè)面中間而不是按頁(yè)進(jìn)行滾動(dòng)的效果讼庇。其原因是無論是分頁(yè)滾動(dòng)還是不分頁(yè)滾動(dòng)绎巨,在滾動(dòng)時(shí)都是通過調(diào)整滾動(dòng)視圖的contentOffset來實(shí)現(xiàn)的。而當(dāng)滾動(dòng)視圖進(jìn)行橫豎屏切換時(shí)不會(huì)調(diào)整對(duì)應(yīng)的contentOffset值蠕啄,這樣就導(dǎo)致了在屏幕方向切換時(shí)的滾動(dòng)位置出現(xiàn)異常认烁。解決的辦法就是在屏幕滾動(dòng)時(shí)的相應(yīng)回調(diào)處理方法中修正這個(gè)contentOffset的值來解決這個(gè)問題。比如我們可以在屏幕切換的sizeclass變化的視圖控制器的協(xié)議方法中添加如下代碼:

- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection
{
    [super traitCollectionDidChange:previousTraitCollection];
    
    UIScrollView *scrollView = (UIScrollView*)self.view;
    
    //根據(jù)當(dāng)前的contentOffset調(diào)整到正確的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);
    
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末介汹,一起剝皮案震驚了整個(gè)濱河市却嗡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嘹承,老刑警劉巖窗价,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異叹卷,居然都是意外死亡撼港,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門骤竹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來帝牡,“玉大人,你說我怎么就攤上這事蒙揣“辛铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵懒震,是天一觀的道長(zhǎng)罩息。 經(jīng)常有香客問我,道長(zhǎng)个扰,這世上最難降的妖魔是什么瓷炮? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮递宅,結(jié)果婚禮上娘香,老公的妹妹穿的比我還像新娘。我一直安慰自己办龄,他們只是感情好烘绽,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著土榴,像睡著了一般诀姚。 火紅的嫁衣襯著肌膚如雪响牛。 梳的紋絲不亂的頭發(fā)上玷禽,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天赫段,我揣著相機(jī)與錄音,去河邊找鬼矢赁。 笑死糯笙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的撩银。 我是一名探鬼主播给涕,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼额获!你這毒婦竟也來了够庙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤抄邀,失蹤者是張志新(化名)和其女友劉穎耘眨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體境肾,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剔难,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奥喻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片偶宫。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖环鲤,靈堂內(nèi)的尸體忽然破棺而出纯趋,到底是詐尸還是另有隱情,我是刑警寧澤冷离,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布结闸,位于F島的核電站,受9級(jí)特大地震影響酒朵,放射性物質(zhì)發(fā)生泄漏桦锄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一蔫耽、第九天 我趴在偏房一處隱蔽的房頂上張望结耀。 院中可真熱鬧,春花似錦匙铡、人聲如沸图甜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)黑毅。三九已至,卻和暖如春钦讳,著一層夾襖步出監(jiān)牢的瞬間矿瘦,已是汗流浹背枕面。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缚去,地道東北人潮秘。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像易结,于是被迫代替她去往敵國(guó)和親枕荞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容