iOS 快速實現分頁界面的搭建

級別: ★★☆☆☆
標簽:「iOS」「分頁」「QiPageMenuView」
作者: 沐靈洛
審校: QiShare團隊


iOS 快速實現分頁界面的搭建

項目中我們經常會遇到滾動分頁的設計效果,被用來對不同數據界面的展示進行分類。我們先可以來預覽一下實現效果:

實現分析

根據動圖進行實現分析:這個效果的實現分為兩部分頂部的QiPageMenuView和內容展示部分QiPageContentView:

QiPageMenuView是基于UIScrollView實現的,我們可以按照自己的項目需求,定制自己需要實現的效果。QiPageMenuView提供了可設置的屬性有:菜單每一項是否根據文字的大小自適應寬度還是設置固定寬度、菜單的首項和最后一項距離父視圖的間距、每一項之間的間距、包括了每一項QiPageItem的展示效果自定義等。也實現了菜單項超出一屏幕時自動滑動顯示的效果:

QiPageContentView是基于UIPageViewController實現的封裝,在項目中可能有多處這樣的界面效果。單純的使用UIPageViewController寫在相應的控制器中,添加相應的子控制器,通過實現UIPageViewController的數據源和代理協議也可以達到這種效果。但是UIPageViewController嵌套在主控制器中,耦合度比較高,代碼量也比較大,若是多處需要使用這種效果,每次都寫一遍UIPageViewController,效率和可移植性不高。 QiPageMenuView和QiPageContentView之間是解耦合的,彼此都可以作為單獨的控件去使用。在設計構思時,菜單視圖的樣式有可能是QiPageMenuView樣式之外的視圖,若是兩者耦合度太高,就變成了QiPageContentView + QiPageMenuView組合,能展現的效果就被限制了。

QiPageMenuView實現與使用
  1. QiPageMenuView.h中展現了QiPageMenuView可實現定制的相關屬性以及初始化方法介紹。
@interface QiPageMenuView : UIScrollView<QiPageControllerDelegate>
/**菜單欄點擊事件*/
@property (nonatomic,copy)void(^pageItemClicked)(NSInteger clickedIndex,QiPageMenuView *menu);
/**常態item的字體顏色*/
@property (nonatomic,strong)UIColor *normalTitleColor;
/**選中item的字體顏色*/
@property (nonatomic,strong)UIColor *selectedTitleColor;
/**常態item的字體*/
@property (nonatomic,strong)UIFont *titleFont;
/**選中Item的字體*/
@property (nonatomic,strong)UIFont *selectedTitleFont;
/**字體距離item兩邊的間距,itemsAutoResizing = YES時 設置有效*/
@property (nonatomic,assign)CGFloat itemTitlePadding;
/**item距上的間距。itemIsVerticalCentred = NO的時候設置有效*/
@property (nonatomic,assign)CGFloat itemTopPadding;
/**items的左邊縮進*/
@property (nonatomic,assign)CGFloat leftMargin;
/**items的右邊縮進*/
@property (nonatomic,assign)CGFloat rightMargin;
/**是否根據文字的長度自動計算item的width default YES*/
@property (nonatomic,assign)BOOL itemsAutoResizing;
/**item是否垂直居中顯示,默認yes;  itemTopPadding 與 lineTopPadding 不會生效;設置NO itemHeight會自適應高*/
@property (nonatomic,assign)BOOL itemIsVerticalCentred;
/**item之間的間距*/
@property (nonatomic,assign)CGFloat itemSpace;
/**每個item的高度*/
@property (nonatomic,assign)CGFloat itemHeight;
/**每個item的寬度。itemsAutoResizing = YES不必賦值也可。反之必須給值。*/
@property (nonatomic,assign)CGFloat itemWidth;
/**是否顯示下劃線 default YES*/
@property (nonatomic,assign)BOOL hasUnderLine;
/**下劃線顏色*/
@property (nonatomic,strong)UIColor *lineColor;
/**下劃線到item的間距*/
@property (nonatomic,assign)CGFloat lineTopPadding;
/**下劃線的高度*/
@property (nonatomic,assign)CGFloat lineHeight;
/**下劃線的寬度*/
@property (nonatomic,assign)CGFloat lineWitdh;
/**pageController滑動完成*/
@property (nonatomic,assign)NSInteger pageScrolledIndex;
/**初始化方法*/
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray*)titles dataSource:(NSDictionary<QiPageMenuViewDataSourceKey, id> *)dataSource;
- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray*)titles;
/**滑動到某一項 @param pageItem item*/
- (void)scrollToPageItem:(QiPageItem*)pageItem;
/*!@brief 更新標題數組@param items selectedIndex重置選中的item*/
- (void)updateMenuViewWithNewItemArray:(NSArray *)items selectedIndex:(NSInteger)selectedIndex;
@end復制代碼
  1. QiPageMenuView滑動顯示的核心代碼
- (void)scrollToPageItem:(QiPageItem*)pageItem {[self refreshUnderLineViewPosition:pageItem];if (self.contentSize.width <= self.width) {return;}CGRect originalRect = pageItem.frame;CGRect convertRect = [self convertRect:originalRect toView:self.superview];CGFloat targetX;CGFloat realMidX = CGRectGetMinX(originalRect)+CGRectGetWidth(originalRect)/2;if (CGRectGetMidX(convertRect) < CGRectGetMidX(self.frame)) {//是否需要右滑if (realMidX> CGRectGetMidX(self.frame)) {targetX = realMidX-CGRectGetMidX(self.frame);}else {targetX = 0;}[self setContentOffset:CGPointMake(targetX, 0) animated:YES];} else if (CGRectGetMidX(convertRect) > CGRectGetMidX(self.frame)) {if (realMidX+CGRectGetMidX(self.frame)<self.contentSize.width) {targetX = realMidX-CGRectGetMidX(self.frame);} else {targetX = self.contentSize.width - CGRectGetMaxX(self.frame);}[self setContentOffset:CGPointMake(targetX, 0) animated:YES];}
}
復制代碼
  1. QiPageMenuView使用的兩種方式
  • 方式一:
    //定制樣式NSDictionary *dataSource = @{QiPageMenuViewNormalTitleColor : [UIColor blackColor],QiPageMenuViewSelectedTitleColor : [UIColor redColor],QiPageMenuViewTitleFont : [UIFont systemFontOfSize:14],QiPageMenuViewSelectedTitleFont : [UIFont systemFontOfSize:14],QiPageMenuViewItemIsVerticalCentred : @(YES),QiPageMenuViewItemTitlePadding : @(10.0),QiPageMenuViewItemTopPadding : @(20.0),QiPageMenuViewItemPadding : @(10.0),QiPageMenuViewLeftMargin : @(20.0),QiPageMenuViewRightMargin : @(20.0),QiPageMenuViewItemsAutoResizing : @(YES),QiPageMenuViewItemWidth : @(90.0),QiPageMenuViewItemHeight : @(40.0),QiPageMenuViewHasUnderLine :@(YES),QiPageMenuViewLineColor : [UIColor greenColor],QiPageMenuViewLineWidth : @(30.0),QiPageMenuViewLineHeight : @(4.0),QiPageMenuViewLineTopPadding : @(10.0)};QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"消息",@"節日消息",@"廣播通知",@"QISHARE",@"奇舞團"] dataSource:dataSource];menuView.backgroundColor = [UIColor orangeColor];[self.view addSubview:menuView];
復制代碼
  • 方式二:
    QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"系統消息",@"節日消息",@"廣播通知"]];menuView.backgroundColor = [UIColor orangeColor];//定制樣式menuView.normalTitleColor = [UIColor blackColor];menuView.selectedTitleColor = [UIColor redColor];menuView.titleFont = [UIFont systemFontOfSize:14];menuView.selectedTitleFont = [UIFont systemFontOfSize:14];menuView.itemIsVerticalCentred = YES;menuView.itemTitlePadding = 10.0;menuView.itemTopPadding = 20.0;menuView.itemSpace = 10.0;menuView.leftMargin = 20.0;menuView.rightMargin = 20.0;menuView.itemsAutoResizing = YES;menuView.itemWidth = 90;menuView.itemHeight = 40;menuView.hasUnderLine = YES;menuView.lineColor = [UIColor greenColor];menuView.lineWitdh = 30;menuView.lineHeight = 4.0;menuView.lineTopPadding = 10;[self.view addSubview:menuView];
復制代碼
QiPageContentView實現與使用
  1. QiPageContentView.h

@interface QiPageContentView : UIView<UIPageViewControllerDelegate, UIPageViewControllerDataSource,UIScrollViewDelegate>@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *controllerArray; //!< 控制器數組
/**滑動結束:block回調*/
@property (nonatomic,copy)void(^pageContentViewDidScroll)(NSInteger currentIndex,NSInteger beforeIndex,QiPageContentView *pageView);/**滑動結束:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageContentViewDelegate> contentViewDelgate;/**設置滑動至某一個控制器@param index index@param beforeIndex 控制方向*/
- (void)setPageContentShouldScrollToIndex:(NSInteger)index beforIndex:(NSInteger)beforeIndex;/**初始化方法@param frame frame@param childViewControllers childViewControllers@return 實例*/
- (instancetype)initWithFrame:(CGRect)frame childViewController:(NSArray*)childViewControllers;@end
復制代碼
  1. QiPageContentView使用
QiPageContentView *contenView = [[QiPageContentView alloc]initWithFrame:CGRectMake(0, 10, self.view.width, self.view.height - 88-10) childViewController:@[ctrl,ctrl1,ctrl2,ctrl3]];
[self.view addSubview:contenView];
復制代碼
QiPageContentView與QiPageMenuView解耦

QiPageContentView與QiPageMenuView使用各自頭文件中定義的協議或者block屬性實現兩者之間事件交互,從而達到分頁聯動效果;兩者的解耦,使得它們的使用更加靈活。

  1. QiPageMenuView交互事件的傳遞
@protocol QiPageMenuViewDelegate <NSObject>
/**菜單點擊了某個item@param index 點擊了index*/
- (void)pageMenuViewDidClickedIndex:(NSInteger)index beforeIndex:(NSInteger)beforeIndex;@end@interface QiPageMenuView : UIScrollView
/**菜單欄點擊事件:block回調*/
@property (nonatomic,copy)void(^pageItemClicked)(NSInteger clickedIndex,NSInteger beforeIndex,QiPageMenuView *menu);/**菜單欄點擊事件:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageMenuViewDelegate> menuViewDelgate;
復制代碼
  1. QiPageContentView交互事件的傳遞
@protocol QiPageContentViewDelegate <NSObject>/**滑動完成回調@param index 滑動至index*/
- (void)pageContentViewDidScrollToIndex:(NSInteger)index beforeIndex:(NSInteger)beforeIndex;
@end@interface QiPageContentView : UIView<UIPageViewControllerDelegate, UIPageViewControllerDataSource,UIScrollViewDelegate>
@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSArray *controllerArray; //!< 控制器數組
/**滑動結束:block回調*/
@property (nonatomic,copy)void(^pageContentViewDidScroll)(NSInteger currentIndex,NSInteger beforeIndex,QiPageContentView *pageView);
/**滑動結束:代理回調 若實現block代理不會走*/
@property (nonatomic, weak) id<QiPageContentViewDelegate> contentViewDelgate;復制代碼
  1. QiPageContentView和QiPageMenuView組合實現分頁界面
QiPageMenuView *menuView = [[QiPageMenuView alloc]initWithFrame:CGRectMake(0, 0, self.view.width, 50) titles:@[@"系統消息",@"節日消息",@"廣播通知",@"最新",@"最熱"] dataSource:dataSource];
menuView.backgroundColor = [UIColor orangeColor];
[self.view addSubview:menuView];QiPageContentView *contenView = [[QiPageContentView alloc]initWithFrame:CGRectMake(0, menuView.bottom+10, self.view.width, self.view.height - menuView.bottom - 10 - 88-10) childViewController:@[ctrl,ctrl1,ctrl2,ctrl3,ctrl4]];
[self.view addSubview:contenView];menuView.pageItemClicked = ^(NSInteger clickedIndex, NSInteger beforeIndex, QiPageMenuView *menu) {NSLog(@"點擊了:之前:%ld 現在:%ld",beforeIndex,clickedIndex);[contenView setPageContentShouldScrollToIndex:clickedIndex beforIndex:beforeIndex];};contenView.pageContentViewDidScroll = ^(NSInteger currentIndex, NSInteger beforeIndex, QiPageContentView * _Nonnull pageView) {menuView.pageScrolledIndex = currentIndex;NSLog(@"滾動了:之前:%ld 現在:%ld",beforeIndex,currentIndex);};
復制代碼

工程源碼GitHub地址


小編微信:可加并拉入《QiShare技術交流群》。

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:
iOS 中的界面旋轉
iOS 常用布局方式之Frame
iOS 常用布局方式之Autoresizing
iOS 常用布局方式之Constraint
iOS 常用布局方式之StackView
iOS 常用布局方式之Masonry
iOS UIButton根據內容自動布局
奇舞周刊

轉載于:https://juejin.im/post/5d0779595188251c9d45e9e9

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251636.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java中String的常用方法

java中String的常用方法 轉自&#xff1a;http://archer-zhou.iteye.com/blog/443864 java中String的常用方法1、length() 字符串的長度例&#xff1a;char chars[]{a,b.c};String snew String(chars);int lens.length();2、charAt() 截取一個字符例&#xff1a;char ch;ch&quo…

筆試小結---非對稱加密算法

非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey).公開密鑰和私有密鑰是一對,如果公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰進行加密,那么只有用對應的公開密鑰才能解密. 非對稱加密算法的保密性比較好,它消除了最終用戶交換…

登錄令牌 Token 介紹

Token值介紹 token 值: 登錄令牌.利用 token 值來判斷用戶的登錄狀態.類似于 MD5 加密之后的長字符串. 用戶登錄成功之后,在后端(服務器端)會根據用戶信息生成一個唯一的值.這個值就是 token 值. 基本使用: 在服務器端(數據庫)會保存這個 token 值,以后利用這個 token 值來檢索…

java-number

通常&#xff0c;當我使用number類型的時候&#xff0c;我們可以使用原始數據類型例如byte&#xff0c;int,long,double等 int i 5000; float b 13.65; double m 0xaf; 所有包裝類&#xff08;整型&#xff0c;長型&#xff0c;字節型&#xff0c;雙精度型&#xff0c;浮點型&a…

您的瀏覽器沒有獲得Java Virtual Machine(JVM)支持。可能由于沒有安裝JVM或者已安裝但是沒有啟用。請安裝JVM1.5或者以上版本,如果已安裝則啟用它。...

您的瀏覽器沒有獲得Java Virtual Machine(JVM)支持。可能由于沒有安裝JVM或者已安裝但是沒有啟用。請安裝JVM1.5或者以上版本&#xff0c;如果已安裝則啟用它。 https://www.java.com/zh_CN/download/faq/remove_olderversions.xml https://jingyan.baidu.com/article/6d704a13…

指令定義

restict&#xff1a;它告訴AngularJS這個指令在DOM中可以以何種形式被聲明。 E(元素&#xff09; <my-directive> </mydirective>A(屬性) <div my-directive“expression”> </div>C(類名) <div class“my-directive:expression;”> </div>…

MyBatis學習總結(9)——使用MyBatis Generator自動創建代碼

2019獨角獸企業重金招聘Python工程師標準>>> 由于MyBatis屬于一種半自動的ORM框架&#xff0c;所以主要的工作就是配置Mapping映射文件&#xff0c;但是由于手寫映射文件很容易出錯&#xff0c;所以可利用MyBatis生成器自動生成實體類、DAO接口和Mapping映射文件。這…

[BZOJ2125]最短路(圓方樹DP)

題意&#xff1a;仙人掌圖最短路。 算法&#xff1a;圓方樹DP&#xff0c;$O(n\log nQ\log n)$ 首先建出仙人掌圓方樹&#xff08;與點雙圓方樹的區別在于直接連割邊&#xff0c;也就是存在圓圓邊&#xff09;&#xff0c;然后考慮點u-v的最短路徑&#xff0c;顯然就是&#xf…

20162317 2017-2018-1 《程序設計與數據結構》第8周學習總結

20162317 2017-2018-1 《程序設計與數據結構》第8周學習總結 教材學習內容總結 1、二叉查找樹的定義、性質2、向二叉查找樹中添加元素的方法3、在二叉查找樹中刪除元素的方法4、旋轉的定義、方法、意義 教材學習中的問題和解決過程問題1&#xff1a;我在17章中看到這么一句話&a…

ES6模塊的轉碼

瀏覽器目前還不支持ES6模塊,為了實現立刻使用,我們可以將其轉為ES5的寫法.除了Babel可以用來轉碼,還有以下兩個方法也可以用來轉碼: ES6 moudule transpilerSystemJS ES6 moudule transpiler是square公司開源的一個轉碼器,可以將ES6模塊轉為CommonJS模塊或AMD模塊,從而在瀏覽器…

Java基礎學習總結(22)——異常處理

2019獨角獸企業重金招聘Python工程師標準>>> 一、異常的概念 異常指的是運行期出現的錯誤&#xff0c;也就是當程序開始執行以后執行期出現的錯誤。出現錯誤時觀察錯誤的名字和行號最為重要。 1 package cn.javastudy.summary;2 3 public class TestEx{4 5 …

XAML中格式化日期

要求被格式化數據的類型是DateTime StringFormatyyyy-MM-dd StringFormat{}{0:yyyy-MM-dd}轉載于:https://www.cnblogs.com/changbaishan/p/9144584.html

130242014045 林承暉 第2次實驗

軟件體系結構的第二次實驗&#xff08;解釋器風格與管道過濾器風格&#xff09; 一、實驗目的 1&#xff0e;熟悉體系結構的風格的概念 2&#xff0e;理解和應用管道過濾器型的風格。 3、理解解釋器的原理 4、理解編譯器模型 二、實驗環境 硬件&#xff1a; 軟件&#xff1a;P…

AnularJS1事件

在Web應用的組件是松耦合的情況下&#xff0c;比如需要用戶驗證然后處理授權&#xff0c;即時的通信不總是可行的&#xff0c;因為組件沒有耦合在一起。 例如&#xff0c;如果后端對一個請求返回了狀態碼401&#xff08;表明一個未經授權的請求&#xff09;&#xff0c;我們期望…

Java基礎學習總結(8)——super關鍵字

2019獨角獸企業重金招聘Python工程師標準>>> 一、super關鍵字 在JAVA類中使用super來引用父類的成分&#xff0c;用this來引用當前對象&#xff0c;如果一個類從另外一個類繼承&#xff0c;我們new這個子類的實例對象的時候&#xff0c;這個子類對象里面會有一個父類…

conda鏡像

轉自https://blog.csdn.net/guilutian0541/article/details/81004769 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main conda config --set show…

Java基礎學習總結(17)——線程

2019獨角獸企業重金招聘Python工程師標準>>> 一、線程的基本概念 線程理解&#xff1a;線程是一個程序里面不同的執行路徑 每一個分支都叫做一個線程&#xff0c;main()叫做主分支&#xff0c;也叫主線程。 程只是一個靜態的概念&#xff0c;機器上的一個.class文件…

(轉)MySQL自帶的性能壓力測試工具mysqlslap詳解

mysqlslap 是 Mysql 自帶的壓力測試工具&#xff0c;可以模擬出大量客戶端同時操作數據庫的情況&#xff0c;通過結果信息來了解數據庫的性能狀況 mysqlslap 的一個主要工作場景就是對數據庫服務器做基準測試 例如我們拿到了一臺服務器&#xff0c;準備做為數據庫服務器&#x…

node.js HelloWord

創建 server.js var http require("http"); http.createServer(function(req,res){ //設置請求頭的編碼格式 res.writeHead(200,{Content-Type:text/html;charsetutf-8}); //設置網頁的編碼格式&#xff08;防止中文亂碼&#xff09; res.write("<head>&…

JavaScript --- this

介紹: this:引用環境執行的環境對象arguments:一個類數組對象,它包含傳入函數的所以參數callee:arguments對象的一個屬性,該屬性是一個指針,指向擁有arguments對象的函數caller:保存著調用當前函數的函數引用apply()方法:第一個參數是作用域&#xff0c;第二個參數是Array實例…