iOS 自定義導航--參照系統導航

不以圖片開頭的文章都不是好文章

與世無爭.JPG

任何時候,都不要迷失自己。有一雙清澈的眼睛,可以沉默,但不要迷離方向;有一顆干凈的心靈,可以容納,但不能承載太多;有一個優雅的姿態,可以美麗,但不要沉溺世事。其實,一切源于自然,源于清凈,源于靈魂的修行。

現狀描述:

一般項目中大概都是一個tabbar管理幾個帶導航的控制器,可其中有幾個控制器沒有導航。這時候就需要進入該控制器的時候隱藏導航,離開的時候顯示導航。

大概流程就是設置代理 self.navigationController.delegate = self;
遵守協議 UINavigationControllerDelegate

#pragma mark - UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

并在viewWillAppear中隱藏

[self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc] init] forBarMetrics:UIBarMetricsDefault];

viewWillDisappear中顯示導航,設置回導航,例如:

UIImage *image= [self createImageWithColor:kMainColor];
[self.navigationController.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
//其中:createImageWithColor:是根據傳遞主題顏色生成圖片
/*
- (UIImage *)createImageWithColor:(UIColor*)color {
    CGRect rect = CGRectMake(0, 0, 1.0f, 1.0f);
    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context =  UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, rect);
    UIImage *image =  UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}*/
發現Bug:

以上這種寫法,一般也不會有啥問題。除非遇到跳轉的界面也是隱藏導航的,這時候有概率出現該顯示導航的卻消失了(復現過程:滑動來回調用viewWillAppearviewWillDisappear

腦袋瓜子嗡嗡的.jpg

構思解決方案:

所以干脆讓導航一直都是隱藏狀態,顯示的時候添加自定義的導航View,當然功能都參照著系統的功能

實施大致過程關鍵Code:
  • 初始化 懶加載控件
- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initView];
    }
    return self;
}

- (void)initView {
    self.backgroundColor = [UIColor redColor];///kMainColor;
    //根據公開傳遞的參數按需增加
    //默認加載灰色的線
    [self bottomGrayLine];
    [self backBtn];
}
  • 返回按鈕
/// 返回按鈕 默認添加 參照leftBarButtonItem
- (JYDisableHightlightBtn *)backBtn {
    if (!_backBtn) {
        _backBtn = [JYDisableHightlightBtn buttonWithType:(UIButtonTypeCustom)];
        [self addSubview:_backBtn];
        //Masonry約束
        [_backBtn mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(15);
            make.width.equalTo(80);
            make.height.equalTo(44);
            make.bottom.equalTo(0);
            
        }];
        _backBtn.backgroundColor = [UIColor blueColor];
        [_backBtn setImage:[UIImage imageNamed:@"common_back"] forState:(UIControlStateNormal)];
        //按鈕圖片居左
        _backBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        //點擊響應RAC傳遞信號
        [[_backBtn rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
            if (self.leftBtnClickedBlock) { //使用block自定義返回或返回根視圖
                self.leftBtnClickedBlock();
            }else {
                [self.viewController.navigationController popViewControllerAnimated:YES];
            }
        }];
    }
    return _backBtn;
}
  • 中間的標題
//中間的標題
- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc]init];
        [self addSubview:_titleLabel];
        [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.equalTo(self.mas_centerX);
            make.bottom.equalTo(-10);
        }];
        _titleLabel.backgroundColor = [UIColor blueColor];
        _titleLabel.font = [UIFont systemFontOfSize:18];
        _titleLabel.textColor = [UIColor whiteColor];
    }
    return _titleLabel;
}
  • 在頭文件中公開屬性,方便設置導航按鈕等,如:
//背景
@property (nonatomic, copy) NSString *jy_naviImageName; //titleView
@property (nonatomic, assign) BOOL jy_hideBottomGrayLine;
//左邊
@property (nonatomic, copy) void (^leftBtnClickedBlock)(void); //left
@property (nonatomic, assign) BOOL jy_hiddenBackBtn;

@property (nonatomic, copy) void (^leftBtnsClickedBlock)(UIButton *leftBtns); //lefts需要

//中間
@property (nonatomic, copy) NSString *titleString;
@property (nonatomic, copy) NSString *titleImageString;
  • 當然還要仿照系統UINavigationItem還有titleViewrightBarButtonItemrightBarButtonItems以及是否隱藏返回按鈕,右側是否可點擊等等

  • 最后在基類中隱藏導航并添加自定義導航視圖,后續創建的控制器需要繼承自該基類
/*
 設置父類的導航代理,隱藏導航,并添加自定義導航
 */
#import "JYBaseViewController.h"

@interface JYBaseViewController ()<UINavigationControllerDelegate>

@end

@implementation JYBaseViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.delegate = self;
    self.baseNaviView = [[JYNaviView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, k_Height_NavBar)];
    [self.view addSubview:self.baseNaviView];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [self.navigationController setNavigationBarHidden:YES animated:YES];
}

#pragma mark - dealloc
- (void)dealloc{
    NSLog(@"%@--dealloc", [self className]);
}
整合到項目中實際遇到的問題:
  1. UISearchControllersearchBar點擊的時候會跑到頂部
    解決辦法:個人建議直接使用UISearchBar設置樣式后調用原來的邏輯。
  2. 同一個界面直接顯示和配合WMPageController使用。
    解決辦法:在其中任意一個推出界面時增加type屬性(或成員變量),根據屬性來判斷是否隱藏導航視圖。如:
初始化賦值type,枚舉最好,此處簡寫
- (instancetype)initWithWMPage:(NSInteger)type {
    if (self = [super init]) {
        _type = type;
    }
    return self;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    if (self.type == 1) {
        self.baseNaviView.hidden = YES;
    }else {
        self.baseNaviView.titleString = @"微視頻";
        self.baseNaviView.hidden = NO;
    }
    //...

    CGFloat startY = self.baseNaviView.hidden ? 0 : CGRectGetMaxY(self.baseNaviView.frame);
    CGFloat videoH = self.baseNaviView.hidden ? ScreenHeight - self.tabBarController.tabBar.height- k_Height_NavBar - 41 : ScreenHeight- k_Height_NavBar- k_Height_Bottom;
    self.tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, startY, ScreenWidth, videoH) style:(UITableViewStylePlain)]; //只有導航沒有tabbar,劉海屏系列減34,非減0
}
  1. 有的界面不顯示自定義的導航視圖
    解決辦法:查看視圖層級,如果被遮擋那就帶到最前方[self.view bringSubviewToFront:self.baseNaviView];

  2. 子視圖被遮擋
    解決辦法:如果是代碼創建的視圖,那么創建的時候CGRectMake的Y參數使用CGRectGetMaxY(self.baseNaviView.frame)或者k_Height_navBar,高度看情況是否需要再減掉。如果是Xib拖的控件,那么建議top約束到superView而不是Safe Area,然后在控制器中self.safeToTop.constant = k_Height_NavBar;

  3. 執行Block會警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
    解決辦法:建議導入RAC框架或YYCategories,創建弱引用self

    @weakify(self)
    self.baseNaviView.leftBtnClickedBlock = ^{
        @strongify(self)
        [self.navigationController popToRootViewControllerAnimated:YES];
    };

...

  • BTW:Demo
    煩惱都忘掉.JPG
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容