自定義特殊導航欄(啊哈哈)

在項目開發中遇到個比較特殊的需求,即一下圖所顯示的
圖片描述dom

第一?看到這樣的圖
圖片描述ide

第一次溝通 atom

圖片一直傳不上次,此刻心情自行體會吧spa

第二次--卒
圖片描述設計

第三次
圖片描述代理

原因
設計師認爲當前蘋果的基於導航欄左側返回的方式在大屏幕手機上操做,用戶須要經過雙手才能更好的操做,單手操做時並不能很好的從左邊返回以及容易掉地上 ,啊哈哈 因此想打破傳統的設計,設計了返回按鈕在右邊的方式(__?開發__), 做爲一個苦逼開發,爭取無果後,那就只能 去實現嘍code

分析

從表面上看,只是更改裏系統導航控制器item按鈕的位置 可是。。。。。orm

第一次嘗試----修改頂部按鈕位置

當時想經過簡單的修改按鈕的位置來達到消息,可是。。。。控制器在push與pop操做時,應該如何修改頂部的按鈕事件

  • 問題1 : 如何保持返回按鈕永遠在控制器上,而且導航控制器的根控制器上不存在圖片

  • 問題2 : 有些界面有更多按鈕,有些界面無更多按鈕,如何處理

第二次嘗試----重寫導航控制器

既然以上方式沒法達到功能,那麼要不就重寫一個導航控制器? 啊哈哈 提及來簡單 可是作起來 導航控制器須要控制不少的細節,若是重寫的話,那麼主要是導航頭,如何重寫

第三次嘗試----利用runtime來實現

  1. 經過擴展UIViewController的方式來實現
    須要添加利用runtime機制,添加兩個屬性

    • 頭文件定義

    @property(nonatomic,strong) UIColor *titleColor;
    
    @property(nonatomic,strong) NSString *subTitle;
    
    //設置帶有圖片的title而且圖片添加事件  
    -(void)setTitle:(NSString *)title titleIcon:(UIImage *)image iconAction:(SEL)action;
    • 實現

-(void)setTitle:(NSString *)title
{
    if([self isKindOfClass:[UIAlertController class]]){
        [self setValue:title forKey:@"_title"];
        
        return;
    }
    UIView *customView =  self.navigationItem.leftBarButtonItem.customView;
    if (customView == nil) {
        UIButton *label = [[UIButton alloc]init];
        label.tag = 1;
//        [label setText:title];
        [label setTitle:title forState:UIControlStateNormal];
        
        UIFont *font = [UIFont fontWithName:@"PingFangSC-Medium" size:20];
        if (!font) {
            font = [UIFont boldSystemFontOfSize:20];
        }
        label.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
        [label.titleLabel setFont:font];
        [label sizeToFit];
        [label setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        if(label.width > [UIScreen mainScreen].bounds.size.width *2 / 3)
        {
            label.width = [UIScreen mainScreen].bounds.size.width *1 / 3;
        }
        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:label];
    }else if([customView isMemberOfClass:[UIButton class]])
    {
        UIButton *label = (UIButton *)customView;
//        [label setText:title];
        [label setTitle:title forState:UIControlStateNormal];
        [label.imageView setContentMode:UIViewContentModeScaleAspectFill];
        [label sizeToFit];
        [label setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }else
    {
        UIButton *label;
        for (UIView *subView in customView.subviews) {
            UIButton *subLabel = (UIButton *)subView;
            if (subLabel.tag  == 1) {
                label = subLabel;
            }
        }
//        [label setText:title];
        [label setTitle:title forState:UIControlStateNormal];
        [label setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }
//     [self.navigationItem.leftBarButtonItem.customView setBackgroundColor:[UIColor randomColor]];
}

-(void)setTitle:(NSString *)title titleIcon:(UIImage *)image iconAction:(SEL)action
{
    [self setTitle:title];
    UIImage *newImage = nil;
    if (image.size.width > 35 || image.size.height > 35) {
        newImage =[image scaleImageWithSize:CGSizeMake(35, 35) isCircle:YES];
    }else
    {
        newImage = [image scaleImageWithSize:image.size isCircle:YES];
    }
    
    UIView *customView =  self.navigationItem.leftBarButtonItem.customView;
    if (customView != nil && [customView isMemberOfClass:[UIButton class]]) {
        UIButton *label = (UIButton *)customView;
        [label setImage:newImage forState:UIControlStateNormal];
        [label.imageView setContentMode:UIViewContentModeScaleAspectFill];
        [label addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
        [label sizeToFit];
        if (label.width > [UIScreen mainScreen].bounds.size.width *2 / 3) {
            label.width =  [UIScreen mainScreen].bounds.size.width *2 / 3;
             label.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 15);
        }else
        {
             label.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -15);
        }
    }
}
-(void)setTitleColor:(UIColor *)titleColor
{
    objc_setAssociatedObject(self, IndieBandNameKey, titleColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    UIView *view = self.navigationItem.leftBarButtonItem.customView;
    if ([view isMemberOfClass:[UIButton class]]) {
        UIButton *label = (UIButton *)view;
        [label.titleLabel setTextColor:titleColor];
    }else
    {
        UIButton *label;
        for (UIView *subView in view.subviews) {
            UIButton *subLabel = (UIButton *)subView;
            if (subLabel.tag  == 1) {
                label = subLabel;
            }
        }
        
        [label.titleLabel setTextColor:titleColor];

    }
//     [self.navigationItem.leftBarButtonItem.customView setBackgroundColor:[UIColor randomColor]];
}

-(UIColor *)titleColor
{
      return objc_getAssociatedObject(self,IndieBandNameKey);
}

-(void)setSubTitle:(NSString *)subTitle
{
    objc_setAssociatedObject(self,IndieBandNameSubTitleKey , subTitle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    UIView *view = self.navigationItem.leftBarButtonItem.customView;

    if ([view isMemberOfClass:[UIButton class]]) {
        UIView *backView = [[UIView alloc]init];
        
        UIButton *Label = (UIButton *)view;
        
        UIButton *titleLabel = [[UIButton alloc]init];
        titleLabel.tag = 1;
//        [titleLabel setText:Label.text];
        [titleLabel setTitle:Label.titleLabel.text forState:UIControlStateNormal];
        [titleLabel.titleLabel setFont:Label.titleLabel.font];
        [titleLabel sizeToFit];
        [titleLabel.imageView setContentMode:UIViewContentModeScaleAspectFill];
        [titleLabel setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        if(titleLabel.width > [UIScreen mainScreen].bounds.size.width *2 / 3)
        {
            titleLabel.width = [UIScreen mainScreen].bounds.size.width *1 / 3;
        }
        
        UILabel *subTitleLabel = [[UILabel alloc]init];
        subTitleLabel.tag = 2;
         [subTitleLabel setText:subTitle];
        UIFont *font = [UIFont fontWithName:@"PingFangSC-Medium" size:10];
        if (!font) {
            font = [UIFont boldSystemFontOfSize:10];
        }
        
        [subTitleLabel setFont:font];
        [subTitleLabel sizeToFit];
        
        [backView addSubview:titleLabel];
        [backView addSubview:subTitleLabel];
        
        titleLabel.x = 0;
        subTitleLabel.x = titleLabel.width + 3;
        if (iPhone6Plus) {
             subTitleLabel.y = titleLabel.height - subTitleLabel.height - 10;
        }else if(iPhone6){
            subTitleLabel.y = titleLabel.height - subTitleLabel.height - 10;
        }else{
             subTitleLabel.y = titleLabel.height - subTitleLabel.height - 7;
        }
       
        
        backView.width =titleLabel.width + subTitleLabel.width + 5;
        backView.height = titleLabel.height;
        
//        [subTitleLabel setBackgroundColor:[UIColor randomColor]];
//        [backView setBackgroundColor:[UIColor randomColor]];
//        [titleLabel setBackgroundColor:[UIColor randomColor]];
        
        self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:backView];
    }
    else
    {
        UIButton *label;
        for (UIView *subView in view.subviews) {
            UIButton *subLabel = (UIButton *)subView;
            if (subLabel.tag  == 2) {
                label = subLabel;
            }
        }
        
        [label setTitle:subTitle forState:UIControlStateNormal];
        [label setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }
}

-(NSString *)subTitle
{
    return objc_getAssociatedObject(self,IndieBandNameSubTitleKey);
}
以上方式也不是很難, 就是一直在判斷`navigationItem.leftBarButtonItem`以及修改其中子控件的過程
  1. 在自定義的NavigationController中重寫pushViewController:animated:方法, 在其中處理按鈕的個數以及返回按鈕是否顯示的問題

-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    //取消掉系統的back item
    viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:[[UIView alloc]init]];
    
    //push操做  
    [super pushViewController:viewController animated:animated];
    //push 完成後處理
    if ([self.viewControllers count] > 1) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button addTarget:self action:@selector(popButtonClick) forControlEvents:UIControlEventTouchUpInside];
        [button setImage:[UIImage imageNamed:@"topbar_back_btn_n"] forState:UIControlStateNormal];
        [button setImage:[UIImage imageNamed:@"topbar_back_btn_h"] forState:UIControlStateHighlighted];
        [button sizeToFit];
        UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithCustomView:button];
        item.tag = 10008;
        //說明自己就有item (好比:更多 item 等其餘item )
        if (viewController.navigationItem.rightBarButtonItems.count > 0) {
            NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:viewController.navigationItem.rightBarButtonItems];
            [mutableArray insertObject:item atIndex:0];
            viewController.navigationItem.rightBarButtonItems = mutableArray.copy;
        }else
        {
            viewController.navigationItem.rightBarButtonItem = item;
        }
    }
}

雖然上邊的方法解決的問題,可是又引入了新的問題:

1. 左滑手勢的消失

在自定義的導航控制器的viewDidLoad方法中經過如下方式添加左滑手勢

id target = self.interactivePopGestureRecognizer.delegate;
    UIScreenEdgePanGestureRecognizer *GestureRecognizer = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:target action:@selector(handleNavigationTransition:)];
    GestureRecognizer.edges = UIRectEdgeLeft;
    GestureRecognizer.delegate = self;
    // 給導航控制器的view添加全屏滑動手勢
    [self.view addGestureRecognizer:GestureRecognizer];
    // 禁止使用系統自帶的滑動手勢
    self.interactivePopGestureRecognizer.enabled = NO;
    //隱藏back item
    self.navigationItem.hidesBackButton = YES;

同時實現代理方法,禁止在根控制器的時候再觸發手勢

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (self.childViewControllers.count == 1) {
        return NO;
    }
    return YES;
}

2. 屢次跳轉會出現多個返回按鈕

網上看到有人經過對UIControl進行擴展實現,雖然這種方式能夠實現可是並不能通用,控制器的跳轉是能夠經過多種形式實現的,好比頂部item ,UIbutton , UITableViewCell 等等方式實現,因此最好的方式是在導航控制器中實現處理

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated方法中實現限制

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
 {
   //此處代碼能夠不須要
   if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
      self.interactivePopGestureRecognizer.enabled = NO;
    }
    
    //主要在此處代碼,定義一個屬性,當push 的時候判斷是否爲yes
    if (self.pushing) {
        return;  
    }
    
    //具體的處理業務  
    
    [super pushViewController:viewController animated:animated];
    self.pushing = yes;
}

須要在代碼方法中在push動做完成後,將其設置爲NO

-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    self.pushing = NO;   
}

總結

以上就是解決方案了,若是有更好的方式實現,還但願能告知於我
項目趕得太緊,代碼寫的有點渣,噴請輕噴, 啊哈哈

相關文章
相關標籤/搜索