iOS项目重构

前言

由于之前项目比较急,所以对界面的布局没有太多的要求,遗留下很多的问题,现在一直在做重构的工作,并不是说把代码写得如何的完美,而是把代码的逻辑梳理得更加的清楚,更加便于后期迭代开发或者是维护。重构分为了UI重构和逻辑重构,今天讲的是一个UI重构的问题,在项目中纠结了一段时间,选择何种方式来布局,会比较好管理;下面来说一下我的想法;

 

(一)场景;

界面头部有一个tab切换的功能,中间部分的ui是提供给用户输入时间信息的,然后还有一个查询的按钮;底部是带tab的列表;主要要解决的问题就是头部和底部tab切换的逻辑管理。

(二)我的思路;

按照常理,现在很多市面上的app,只要包括有tab的列表大部分采用的是scrollview滚动切换,再配合tab点击的切换;还有一种就是使用addChildViewController,然后配合tab点击进行切换(主要的缺点就是,不能左右滑动切换)。我的这个场景里面显然的包含两级切换逻辑,两层切换逻辑都是用scrollView来控制的话,就会让人感觉左右切换的效果太多,有点不太舒服;最终我采用的是头部tab使用addChildViewController来控制显示底部的带tab的列表控制器,然后底部的控制器中通过scrollView来控制其子控制器。

(三)关键代码;

这个NTContainViewController控制器,就是底部带tab的容器控制器,可以设置子控制器以及tab标签。

#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:^{ 

        } 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的基本使用,上下拉刷新。

转载于:https://my.oschina.net/u/1450995/blog/751181

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值