項目結構更加清晰,方便以后調試bug
怎么讓項目結構更加清晰,誰的事情誰管理
-
分析項目架構
- 方便處理多人開發
- 讓更多的功能復用
- 讓代碼的結構更加清晰
-
自定義類的思路
- 當系統的某些類不能滿足需求的時候,需要給系統的類添加某些功能,但還要保持原有類的功能,采用自定義類,繼承系統的類(自定義控件,自定義模型)
- 讓項目的結構更加清晰,誰的事情誰管理,采取自定義類,以后這個類的問題,可以馬上定位到這個類做的哪些方法(自定義控制器)
-
復習程序的運行
- main -> UIApplicationMain
- 創建UIApplication對象
- 創建UIApplication對象的代理
- 開啟主運行循環,保持程序一直運行
- 加載info.plist文件,判斷下是否指定main
-
關于窗口的建立
- 創建窗口
- 創建窗口的根控制器
- 添加子控制器
- 讓窗口顯示
- 代碼實現 (去消info中Main)
// 1.創建窗口
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 2.創建窗口的跟控制器
// 創建tabBarVc
UITabBarController *tabBarVc = [[UITabBarController alloc] init];
// 設置窗口的根控制器
self.window.rootViewController = tabBarVc;
// 添加子控制器
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = [UIColor redColor];
[tabBarVc addChildViewController:vc];
...
// 3.讓窗口顯示
[self.window makeKeyAndVisible];
-
搭建主架構(讓程序更清晰化)
- 不同的頁面交給不同的控制器管理,并放在不同的文件夾中。
結構清晰化,誰的控件誰管理。
-
自定義UITabBarController
- 內部添加子控制器 可以進行自定義方法,來抽取相同代碼,簡化程序
- 自定義 子控制器,誰的控件,誰管理,方便管理子控制器- 關于主流框架 (UITabBarController 和 UINavigationController) 主要是上面一個條,下面一個條。
- 一般是在 UITabBarController上添加UINavigationController 這樣的話, 在UINavigationController上push子控制器的話 (這樣的話上面的條可以叫給push進去的子控制器進行管理)
-
注意, 每運行一步,都都要看看所運行的效果,滿不滿足自己的需求。
- 此處由于美工的的圖片(tabBar的圖標圖片,大于tabBar的寬度,所以沒有顯示出想要的效果)【tabBar的寬度44 < 圖片高度】
所以需要自定義tabBar,因為系統的tabBar不滿足需求
- 此處由于美工的的圖片(tabBar的圖標圖片,大于tabBar的寬度,所以沒有顯示出想要的效果)【tabBar的寬度44 < 圖片高度】
- 自定義TabBar,取代原有tabBar,并實現原有tabBar的功能
- 有多少按鈕控件,控件的樣式有什么決定。 (對于tabBar,它的樣式是有對應控制器決定的,可以這么說,對應控制器,將所設置的樣式素材,存放在
tabBarItem 屬性中
tabBarItem就是一個模型數據,tabBar從模型數據中讀取了素材,再賦值到它對應的位置上) - 所以底層實現我認為是這么幾步。
- 從外界讀取模型數據,有多少控制器,應該就有多少組模型數據。
- 提取模型數據,并將其賦值給子控件button上。
- 給子控件設置位置
- 點擊按鈕,跳轉到對應的控制器。
- 當然,還有一些細節需要處理,比如按鈕不需要高亮指示,只需要選中和正常兩種狀態, (所以需要自定義按鈕,重寫
- (void)setHighlighted:(BOOL)highlighted
方法,為空即可,讓其在高亮時不做任何操作);按鈕默認選中第一個;選中一個那么前一個就要取消選中。 - 具體代碼如下。
- 有多少按鈕控件,控件的樣式有什么決定。 (對于tabBar,它的樣式是有對應控制器決定的,可以這么說,對應控制器,將所設置的樣式素材,存放在
// 由于不確定什么時候有數據加入,所以采用懶加載方式,進行items的加載,加載完成后才進行按鈕的創建以及賦值操作,不需要在視圖 一創建的時候,就取創建按鈕, 節約功耗
- (void)setItems:(NSArray *)items
{
_items = items;
for (UITabBarItem *item in items) {
// 從items中取出模型數據,并將其賦值給對應按鈕
UIButton *btn = [LXLTabBarButton buttonWithType:UIButtonTypeCustom];
// 設置內容
[btn setBackgroundImage:item.image forState:UIControlStateNormal];
[btn setBackgroundImage:item.selectedImage forState:UIControlStateSelected];
[btn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchDown];
btn.tag = self.subviews.count;
if (self.subviews.count == 0) {
// 為了程序之處,沒有子控件的時候,按鈕就已經點擊。
[self btnClick:btn];
}
[self addSubview:btn];
}
}
// 進行子控件的布局, 根據子控件的個數,遍歷對每個子控件進行布局
- (void)layoutSubviews
{
[super layoutSubviews];
int count = (int)self.subviews.count;
CGFloat btnW = self.bounds.size.width / count;
CGFloat btnH = self.bounds.size.height;
CGFloat btnY = 0;
CGFloat btnX = 0;
// 布局按鈕的位置
for (int i = 0; i < count; i++) {
UIButton *btn = self.subviews[i];
btnX = i * btnW;
btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
}
}
// 監聽點擊事件, 由于是逆向傳值,所以需要代理,來完成點擊事件處理,并且傳入參數(點擊的是哪個按鈕)tag ,來完成讓控制器的跳轉
- (void)btnClick:(UIButton *)btn
{
_selBtn.selected = NO;
btn.selected = YES;
_selBtn = btn;
// 通知代理點擊了哪個角標的按鈕
if ([_delegate respondsToSelector:@selector(tabBar:didClickBtn:)]) {
[_delegate tabBar:self didClickBtn:btn.tag];
}
}
- 對于父控件,tabBarController
- 創建自定義tabBar控件,并將其作為子控件放置在,原系統tabBar之上,蓋住系統tabBar。(需要注意的是,要移除系統原有的tabBar,但是系統底部處理的時候并不是馬上移除,會在過一段事件才進行移除操作)。
- 需要將對應的模型數組數據(懶加載),傳入,屬于順傳
- 作為代理,完成對應的方法,進行控制器的跳轉
- 具體代碼如下
// 取得模型數據,并存放在模型數組中
- (void)setUpOneChildViewController:(UIViewController *)vc image:(UIImage *)image selImage:(UIImage *)selImage
{
// 設置tabBarButton的圖片,tabBarButton的內容由對應的子控制器的tabBarItem
vc.tabBarItem.image = image;
vc.tabBarItem.selectedImage = selImage;
// 保存對應子控制器的UITabBarItem
[self.items addObject:vc.tabBarItem];
[self addChildViewController:vc];
}
- (void)setUpTabBar
{
// 1.移除系統的tabBar,移除系統自帶的tabBarButton
[self.tabBar removeFromSuperview];
NSLog(@"%@",self.tabBar);
// 2.添加自己的tabBar
LXLTabBar *tabBar = [[LXLTabBar alloc] init];
tabBar.delegate = self;
// tabBar按鈕的個數,由tabBar子控制器個數決定
// tabBar.count = (int)self.childViewControllers.count;
// 傳對應子控制器的tabBarItem數組
tabBar.items = self.items;
tabBar.backgroundColor = [UIColor greenColor];
tabBar.frame = self.tabBar.frame;
[self.view addSubview:tabBar];
}
// 遵守協議實現代理方法,進行界面的跳轉。
#pragma mark -LXLTabBarDelgate方法
// 當點擊tabBar上的條的時候調用
- (void)tabBar:(XMGTabBar *)tabBar didClickBtn:(NSInteger)index
{
// 切換界面
self.selectedIndex = index;
}
- 對于跳轉按鈕的添加
- 我覺得有兩種方案
- 添加到最后一個cell上去
- 直接添加到collectionView上去。
- 第一種方案的實現
- 我覺得有兩種方案
// 判斷是否是最后一個cell,可以根據indexPath進行判斷
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
...
// 給最后一個cell添加一個立即體驗按鈕
// 告訴cell是否是最后一個
// 宏定義數目為4
[cell setIndexPath:indexPath count:LXLPage];
// [cell setIndexPath:indexPath count:4]
// if (indexPath.item == XMGPage - 1) {
// // 添加立即體驗按鈕
//
// }
return cell;
}
- cell內部拿到
indexPath
和count
進行判斷
// 用來判斷下當前cell對象是否是最后一個cell
- (void)setIndexPath:(NSIndexPath *)indexPath count:(int)count
{
if (indexPath.item == count - 1) {
// 最后一個cell
// 添加一個立即體驗按鈕,首先保存整個cell只有一個體驗按鈕
// 顯示這個按鈕
self.startBtn.hidden = NO;
}else{ // 不是最后一個cell
// 隱藏這個按鈕,涉及到循環引用,否則就會被別的cell所引用。
self.startBtn.hidden = YES;
}
}
- 由于cell的按鈕是用的時候才進行顯示,(所以可以進行懶加載
)
- (UIButton *)startBtn
{
if (_startBtn == nil) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
_startBtn= btn;
[btn setBackgroundImage:[UIImage imageNamed:@"guideStart"] forState:UIControlStateNormal];
// 尺寸可以進行自適應'自己根據圖片來安排大小'
[btn sizeToFit];
// 位置確定
btn.center = CGPointMake(self.width * 0.5 , self.height * 0.9);
[self.contentView addSubview:btn];
}
return _startBtn;
}
- 對于控制器之間的跳轉,點擊按鈕跳轉到新控制器。
- 沒有任何聯系的控制器跳轉,直接跳轉即可。 創建到主窗口根控制器顯示,代替原有主窗口根控制器
// 點擊立即體驗按鈕的時候調用
- (void)start
{
// 跳轉到主框架界面。界面之間跳轉,導航控制器,tabBarVc,modal
// 不能使用modal原因:新特性界面一直存在,被窗口的根控制器一直強引用
LXLTabBarController *vc = [[LXLTabBarController alloc] init];
// 設置窗口的根控制器為主框架控制器
[UIApplication shareApplication].keyWindow.rootViewController = vc;
}
- 第二種方法(代碼實現)
- (viod)viewDidLoad{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"guideStart"] forState:UIControlStateNormal];
[btn sizeToFit];
// 初始化位置直接設置在 最終位置,而且只創建一次,不必思考情況
btn.center = CGPointMake(self.collectionView.width * (LXLPageCount - 0.5), self.collectionView.height * 0.9);
[self.collectionView addSubview:btn];
[btn addTarget:self action:@selector(btnClick) forControlEvents:UIControlEventTouchUpInside];
}
// 跳轉控制器
- (void)btnClick
{
LXLTabBarController *vc =[[LXLTabBarController alloc]init];
[UIApplication sharedApplication].keyWindow.rootViewController =vc;
}
- 總結一下,我覺得第二個方法好,因為是控制器進行跳轉,我覺得完全是控制器自己的事,交給它完全是可以處理的。如果交給cell的話,還需要額外的設置,以及判斷清空。從代碼量來看,還是直接設置好一點。