UIScrollView的pagingEnabled屬性用於控制是否按分頁進行滾動。在一些應用中會應用到這一個特性,最典型的就是手機桌面的應用圖標列表。這些界面中每每每一頁功能都比較獨立,系統也提供了UIPageViewController來實現這種分頁滾動的功能。 實現分頁滾動的UI實現通常是最外層一個UIScrollView。而後UIScrollView裏面是一個整體的容器視圖containerView。容器視圖添加N個頁視圖,對於水平分頁滾動來講容器視圖的高度和滾動視圖同樣,而寬度則是滾動視圖的寬度乘以頁視圖的數量,頁視圖的尺寸則和滾動視圖保持一致,對於垂直分頁滾動來講容器視圖的寬度和滾動視圖同樣,而高度則是滾動視圖的高度乘以頁視圖的數量,頁視圖的尺寸則和滾動視圖保持一致。每一個頁視圖中在添加各自的條目視圖。總體效果圖以下: git
根據上面的UI結構這裏用AutoLayout的代碼來實現水平分頁的滾動。這裏的約束設置代碼是iOS9之後提供的相關API。github
- (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<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++)
{
//創建頁視圖
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;
//這裏能夠爲每一個頁視圖添加不一樣的條目視圖,具體實現你們自行添加代碼吧。
}
複製代碼
下面是運行時的效果圖: bash
你也能夠用MyLayout佈局庫來實現分頁滾動的能力。MyLayout佈局庫是筆者開源的一套功能強大的UI佈局庫。 您能夠從github地址: github.com/youngsoft/M… 下載或者從podfile中導入:佈局
pod 'MyLayout'
複製代碼
來使用MyLayout。下面是具體用MyLayout來實現分頁滾動的代碼。fetch
//
#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;
//創建一個水平線性佈局容器視圖
MyLinearLayout *containerView = [MyLinearLayout linearLayoutWithOrientation:MyOrientation_Horz];
containerView.myVertMargin = 0; //水平線性佈局的上下邊界和滾動視圖保持一致,這裏也會肯定線性佈局的高度。
containerView.gravity = MyGravity_Vert_Fill | MyGravity_Horz_Fill; //設置線性佈局中的全部子視圖均分和填充線性佈局的高度和寬度。
[scrollView addSubview:containerView];
//添加頁視圖
NSArray<UIColor*> *colors = @[[UIColor redColor],[UIColor greenColor], [UIColor blueColor]];
NSMutableArray<UIView*> *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中的流式佈局MyFlowLayout所具有的能力和flex-box類似,甚至有些特性要強於後者。流式佈局用於一些子視圖有規律排列的場景,就好比本例子中的滾動分頁的圖標列表的能力。下面就是具體的實現代碼。flex
- (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來實現相同的功能要簡潔和容易得多。下面是程序運行的效果: spa
對於帶有分頁功能的滾動視圖來講,當須要支持橫豎屏時就有可能會出現橫豎屏切換時界面停留在兩個頁面中間而不是按頁進行滾動的效果。其緣由是不管是分頁滾動仍是不分頁滾動,在滾動時都是經過調整滾動視圖的contentOffset來實現的。而當滾動視圖進行橫豎屏切換時不會調整對應的contentOffset值,這樣就致使了在屏幕方向切換時的滾動位置出現異常。解決的辦法就是在屏幕滾動時的相應回調處理方法中修正這個contentOffset的值來解決這個問題。好比咱們能夠在屏幕切換的sizeclass變化的視圖控制器的協議方法中添加以下代碼:code
- (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);
}
複製代碼