前言app
因爲以前項目比較急,因此對界面的佈局沒有太多的要求,遺留下不少的問題,如今一直在作重構的工做,並非說把代碼寫得如何的完美,而是把代碼的邏輯梳理得更加的清楚,更加便於後期迭代開發或者是維護。重構分爲了UI重構和邏輯重構,今天講的是一個UI重構的問題,在項目中糾結了一段時間,選擇何種方式來佈局,會比較好管理;下面來講一下個人想法;ide
(一)場景;函數
界面頭部有一個tab切換的功能,中間部分的ui是提供給用戶輸入時間信息的,而後還有一個查詢的按鈕;底部是帶tab的列表;主要要解決的問題就是頭部和底部tab切換的邏輯管理。佈局
(二)個人思路;ui
按照常理,如今不少市面上的app,只要包括有tab的列表大部分採用的是scrollview滾動切換,再配合tab點擊的切換;還有一種就是使用addChildViewController,而後配合tab點擊進行切換(主要的缺點就是,不能左右滑動切換)。個人這個場景裏面顯然的包含兩級切換邏輯,兩層切換邏輯都是用scrollView來控制的話,就會讓人感受左右切換的效果太多,有點不太舒服;最終我採用的是頭部tab使用addChildViewController來控制顯示底部的帶tab的列表控制器,而後底部的控制器中經過scrollView來控制其子控制器。atom
(三)關鍵代碼;code
這個NTContainViewController控制器,就是底部帶tab的容器控制器,能夠設置子控制器以及tab標籤。orm
#import <UIKit/UIKit.h> @interface NTContainViewController : UIViewController - (instancetype)initWithTitle:(NSString *)title andSubTitles:(NSArray *)subTitles andControllers:(NSArray *)controllers; - (instancetype)initWithTitle:(NSString *)title andSubTitles:(NSArray *)subTitles andControllers:(NSArray *)controllers topOffset:(CGFloat)topOffset; @property (nonatomic, assign) CGFloat topOffset; - (void)refreshTableView; #import "NTContainViewController.h" #import "NTSubViewController.h" #import "NTTitleBarView.h" @interface NTContainViewController () <UIScrollViewDelegate> @property (nonatomic, strong) UIScrollView *containScroll; @property (nonatomic, strong) NSArray *controllers; @property (nonatomic, strong) NSArray *subTitles; @property (nonatomic, strong) NSString *navigationTitle; @property (nonatomic, strong) NTTitleBarView *titleBarView; @property (nonatomic, assign) NSInteger currentIndex; @end @implementation NTContainViewController - (instancetype)initWithTitle:(NSString *)title andSubTitles:(NSArray *)subTitles andControllers:(NSArray *)controllers { return [self initWithTitle:title andSubTitles:subTitles andControllers:controllers topOffset:0]; } - (instancetype)initWithTitle:(NSString *)title andSubTitles:(NSArray *)subTitles andControllers:(NSArray *)controllers topOffset:(CGFloat)topOffset { self = [super init]; if (self) { self.subTitles = subTitles; self.navigationTitle = title; self.controllers = controllers; self.topOffset = topOffset; } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightGrayColor]; if (_navigationTitle) { self.navigationItem.title = _navigationTitle; } self.currentIndex = 0; CGFloat titleBarHieght = 36.0f; self.titleBarView = [[NTTitleBarView alloc] initWithFrame:({ CGRect frame = CGRectMake(0, 0, self.view.frame.size.width, titleBarHieght); frame; }) titles:_subTitles]; [self.view addSubview:_titleBarView]; self.containScroll = [[UIScrollView alloc] initWithFrame:({ CGRect frame = CGRectMake(0, titleBarHieght, self.view.frame.size.width, self.view.frame.size.height - titleBarHieght); frame; })]; _containScroll.delegate = self; _containScroll.bounces = NO; _containScroll.showsHorizontalScrollIndicator = NO; _containScroll.pagingEnabled = YES; [self.view addSubview:_containScroll]; if (_controllers) { [_controllers enumerateObjectsUsingBlock:^(UIViewController *controller, NSUInteger idx, BOOL * _Nonnull stop) { [self addChildViewController:controller]; CGRect childRect = CGRectMake(self.view.frame.size.width * idx, 0, self.view.frame.size.width, self.view.frame.size.height - 64 - 100 - 36); controller.view.frame = childRect; [self.containScroll addSubview:controller.view]; }]; } _containScroll.contentSize = CGSizeMake(self.view.frame.size.width * _controllers.count, _containScroll.frame.size.height); __weak typeof(_containScroll) weakScroll = _containScroll; _titleBarView.titleButtonClicked = ^(NSUInteger index) { [weakScroll setContentOffset:CGPointMake(self.view.frame.size.width * index, 0) animated:NO]; _currentIndex = index; }; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self scrollToStop:YES]; } - (void)scrollToStop:(BOOL)stop { CGFloat offsetX = _containScroll.contentOffset.x; CGFloat scrollWidth = _containScroll.frame.size.width; NSInteger focusIndex = (offsetX + (scrollWidth / 2)) / scrollWidth; if (stop) { [self titleBarViewButtonsAtIndex:focusIndex]; _currentIndex = focusIndex; } } - (void)titleBarViewButtonsAtIndex:(NSInteger)index { UIButton *currentButton = _titleBarView.subviews[index]; [_titleBarView.subviews enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL * _Nonnull stop) { [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; }]; [currentButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; } - (void)refreshTableView { NTSubViewController *currentVC = (NTSubViewController *)_controllers[_currentIndex]; [currentVC refreshTableViewWithType:[NSString stringWithFormat:@"tableView%ld",_currentIndex]]; } @end
下面的這個代碼,是寫在父視圖控制器上面的,有tab切換的view,查詢按鈕,並將上面說到的NTContainViewController控制器當作子控制器並進行管理,經過開發
[strongSelf transitionFromViewController:strongSelf.currentVC toViewController:newViewController duration:0.3f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ rem
} completion:^(BOOL finished) {
[newViewController didMoveToParentViewController:strongSelf];
[strongSelf.currentVC willMoveToParentViewController:nil];
[strongSelf.currentVC removeFromParentViewController];
strongSelf.currentVC = newViewController;
}];函數進行切換。
具體代碼;
#import "NTTestViewController.h" #import "NTContainViewController.h" #import "NTSubViewController.h" #import "NTTitleBarView.h" @interface NTTestViewController () { } @property (nonatomic, strong) NTTitleBarView *testBarView; @property (nonatomic, strong) NSArray *controllers; @property (nonatomic, strong) UIViewController *currentVC; @property (nonatomic, strong) NSInteger currentIndex; @end @implementation NTTestViewController - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @"tab切換"; self.view.backgroundColor = [UIColor whiteColor]; self.testBarView = [[NTTitleBarView alloc] initWithFrame:({ CGRect frame = CGRectMake(0, 64, self.view.frame.size.width, 36); frame; }) titles:@[@"左邊", @"右邊"]]; [self.view addSubview:_testBarView]; UIButton *searchButton = [UIButton buttonWithType:UIButtonTypeCustom]; searchButton.frame = CGRectMake(0, 120, self.view.frame.size.width, 30); [searchButton setTitle:@"查詢" forState:UIControlStateNormal]; searchButton.backgroundColor = [UIColor lightGrayColor]; [searchButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; [searchButton addTarget:self action:@selector(excuteCode) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:searchButton]; [self addChildVC]; __weak typeof(self) weakSelf = self; __strong typeof(self) strongSelf = weakSelf; _testBarView.titleButtonClicked = ^(NSUInteger index) { if (strongSelf.currentIndex != index) { UIViewController *newViewController = strongSelf.controllers[index]; [strongSelf addChildViewController:newViewController]; [strongSelf transitionFromViewController:strongSelf.currentVC toViewController:newViewController duration:0.3f options:UIViewAnimationOptionTransitionCrossDissolve animations:^{ } completion:^(BOOL finished) { [newViewController didMoveToParentViewController:strongSelf]; [strongSelf.currentVC willMoveToParentViewController:nil]; [strongSelf.currentVC removeFromParentViewController]; strongSelf.currentVC = newViewController; strongSelf.currentIndex = index; }]; } }; } - (void)addChildVC { NTSubViewController *subVC1 = [[NTSubViewController alloc] initWithTitle:@"test1"]; NTSubViewController *subVC2 = [[NTSubViewController alloc] initWithTitle:@"test2"]; NTSubViewController *subVC3 = [[NTSubViewController alloc] initWithTitle:@"test3"]; NTContainViewController *testVC1 = [[NTContainViewController alloc] initWithTitle:@"test" andSubTitles:@[@"test1",@"test2",@"test3"] andControllers:@[subVC1, subVC2, subVC3] topOffset:100.0f]; testVC1.view.frame = CGRectMake(0, 64 + 100, self.view.frame.size.width, self.view.frame.size.height - 100 - 64); NTSubViewController *subVC4 = [[NTSubViewController alloc] initWithTitle:@"test4"]; NTSubViewController *subVC5 = [[NTSubViewController alloc] initWithTitle:@"test5"]; NTSubViewController *subVC6 = [[NTSubViewController alloc] initWithTitle:@"test6"]; NTContainViewController *testVC2 = [[NTContainViewController alloc] initWithTitle:@"test" andSubTitles:@[@"test4",@"test5",@"test6"] andControllers:@[subVC4, subVC5, subVC6] topOffset:100.0f]; testVC2.view.frame = CGRectMake(0, 64 + 100, self.view.frame.size.width, self.view.frame.size.height - 100 - 64); self.controllers = @[testVC1, testVC2]; self.currentVC = testVC1; [self addChildViewController:_currentVC]; [self.view addSubview:_currentVC.view]; } - (void)excuteCode { NTContainViewController *currentVC = (NTContainViewController *)_currentVC; [currentVC refreshTableView]; } @end
(四)還有一個關鍵的地方,那就是父子控制器之間的交互;
由於這個界面的主要功能就是經過主視圖的查詢按鈕,操做底部控制器(NTContainViewController控制器中的子控制器),固然最簡單的方法就是發送通知,子控制器接收通知就行;可是我採用的是另外的方法,就是經過NTContainViewController這個容器控制器做爲橋樑,主控制器發送消息至當前顯示的NTContainViewController容器控制器,在經過這個容器控制器找到當前顯示的子控制器,而後發送消息到這個控制器。上面的代碼- (void)excuteCode、- (void)refreshTableView、- (void)refreshTableViewWithType:(NSString *)type;一層一層往下傳遞發送消息的。
總結
整個解決問題的思路,即解決了tab聯動切換的問題,也解決了父控制器向子控制器發送消息的問題,項目中遇到的問題就可以解決,剩下的就只有子控制器的佈局以及獲取數據的部分了,而這個部分所涉及的內容已經不多,就是tableview的基本使用,上下拉刷新。