UIKit高度定制 - UINavigationBar的繼承與定制

UINavigationBar的繼承與定制

我們在iOS項目開發中,有些時候需要修改標準控件的樣式,我們今天就圍繞一個具體項目需求,進行UINavigationBar的繼承與改造。

UIApperance協議屬性定制

我們在UINavigationBar.h頭文件中,看到如下修改NavigationBar背景顏色的屬性

@property(nullable, nonatomic,strong) UIColor *barTintColor NS_AVAILABLE_IOS(7_0) UI_APPEARANCE_SELECTOR;  // default is nil

注意到UI_APPEARANCE_SELECTOR這個宏了么,用這個宏標記的屬性,都是可以通過UIApperance協議進行全局設置的屬性。說的更直白一點,就是可以一次性,修改項目中所有的這個類的默認屬性。

例如在iOS6之前,UILabel的默認背景顏色不是透明色,而是白色。我們就可以使用如下方法,修改UILabel的默認背景色

 [[UILabel appearance] setBackgroundColor:[UIColor clearColor]];

UIApperance協議就是這么神奇,所有的UIKit控件都遵守了這個協議,所有標記了UI_APPEARANCE_SELECTOR宏的屬性,都可以使用appearance實例修改默認值,是不是很炫酷。

項目需求

上面一段與本文正題無關,下面我們看一下本文的項目需求

屏幕快照 2016-01-14 10.44.13.png

分析

這個頁面就是一個標準的NavigationController + TableViewContoller組合實現的設置頁面,導航條和Table的樣式需要訂制。

前面說到的UIApperance協議是可以實現的,我們換一種更為普遍的方式實現,繼承

我們繼承UINavigationBar,創建子類FWBar。我們使用storyboard實例化大體框架模型,并將NavigationViewControllerNavigationBar設置為我們的FWBar類,并將UITableView設置為Static靜態模式,直接編輯了Cell的內容。

屏幕快照 2016-01-14 10.51.44.png

FWBar.m中加入如下代碼

- (void)awakeFromNib
{
    [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsCompact];
    self.shadowImage = [UIImage new];
    
    //把之前的View統統隱藏
    [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj setHidden:YES];
    }];
    
    
    [self addSubview:self.fakeBackgroundView];
    self.fakeBackgroundView.userInteractionEnabled = NO;
    [self sendSubviewToBack:self.fakeBackgroundView];
    
    self.titleTextAttributes = @{
                                 NSFontAttributeName: [UIFont fontWithName:@"NotoSansHans-DemiLight" size:16],
                                 NSForegroundColorAttributeName:[UIColor colorWithRed:57.0/255 green:207.0/255 blue:218.0/255 alpha:1]
                                 };
    
    //rgba(165, 195, 205, 1)
    self.tintColor = [UIColor colorWithRed:165.0/255 green:195.0/255 blue:205.0/255 alpha:1];
}

解釋 因為原生的NaviBar背景View下方有一條灰色的邊,這條邊不是用layer生成的,我沒搞明白是怎么實現的,所以直接將這個View隱藏掉了。順便吧shadowImage也換成空圖。

這里的self.fakeBackgroundView是我們添加的背景,顏色是白色。這里我們將它移到最下層,并且觸摸屬性關掉,userInteractionEnabled設為NO

titleTextAttributes這個屬性,是用來修改title的樣式的。

tintColor這個屬性,是用來修改導航條左右按鈕顏色的。

這些操作做完,還不夠。

我們無法通過暴露出來的接口修改左右按鈕的字體和位置。這也是我們選擇繼承而不是UIApperance的原因

繼承大殺器,高度自定義

- (void)didAddSubview:(UIView *)subview
{
    NSLog(@"%@",subview);
    if ([subview isKindOfClass:NSClassFromString(@"UINavigationButton")]) {
        if ([subview isKindOfClass:[UIButton class]]) {
            
            [(UIButton*)subview setAttributedTitle:[[NSAttributedString alloc] initWithString:[(UIButton*)subview titleForState:UIControlStateNormal] attributes:@{
                                                                                                                                                                   NSFontAttributeName: [UIFont fontWithName:@"AvenirNext-Regular" size:17],
                                                                                                                                                                   NSForegroundColorAttributeName:self.tintColor
                                                                                                                                                                   }] forState:UIControlStateNormal];
        }
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    
    [self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([subview isKindOfClass:NSClassFromString(@"UINavigationButton")]) {
            if ([subview isKindOfClass:[UIButton class]] && subview.frame.origin.x < self.frame.size.width/2) {
                [subview setFrame:({
                    CGRect rect = subview.frame;
                    rect.origin.x = 8;
                    rect.size.width = 69;
                    rect;
                })];
            }
        }
        
    }];
}

解釋 重寫- (void)didAddSubview:(UIView *)subview方法,檢測了系統控件根據NavigationItemNavigationBar添加按鈕這個事件,然后對按鈕進行甄別,定制。

我們找到Cancel這個按鈕,他雖然是UINavigationButton類型,但是一定是繼承了UIButton,所以我們直接強轉成她的父類,修改其文字字體和frame。

重寫layoutSubviews這個方法,是為了實時更新我們的按鈕位置。這個其實也可以不更改的,但是我們的項目需求中,Cancel這個字段太長,字體變大以后導致了顯示不全,所以我們將這個做按鈕的frame變大了。

注意幾點

  1. NSClassFromString(@"UINavigationButton")這個方法是我們無法獲取內部類的時候,獲取Class類型的方法。UINavigationButton這個類名是NSLog輸出時看到的。

  2. 這一段使用了特殊的語法糖,有興趣了解的參考這篇sunnyxx大神的博文,全文搜索關鍵字小括號內聯復合表達式

[subview setFrame:({
                    CGRect rect = subview.frame;
                    rect.origin.x = 8;
                    rect.size.width = 69;
                    rect;
                })];

最后的實現效果。

FWBar.gif

結語

截屏的效果不是太好,細心的朋友可能會發現,我們的FWBarTableView向上滑動的過程中會漸出陰影。

我把這段代碼分享給大家,但是這段代碼偷懶沒用KVO,而是用了ReactiveCocoa這個龐大的龐大框架的小小功能,所以,就沒放到教程里。

- (void)didMoveToSuperview
{
    [super didMoveToSuperview];
    
    UIViewController *presentingViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (presentingViewController.presentedViewController) presentingViewController = presentingViewController.presentedViewController;

    __block BOOL has = NO;
    [[presentingViewController childViewControllers] enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isKindOfClass:[UINavigationController class]]) {
            [[obj childViewControllers] enumerateObjectsUsingBlock:^(__kindof UIViewController * _Nonnull obj2, NSUInteger idx, BOOL * _Nonnull stop) {
                if ([obj2 isKindOfClass:[UITableViewController class]]) {
                    has = YES;
                    UITableViewController* tVC = obj2;
                    if (self.tableViewOffsetDisposable) {
                        [self.tableViewOffsetDisposable dispose];
                    }
                    self.tableViewOffsetDisposable = [RACObserve(tVC.tableView, contentOffset) subscribeNext:^(id x) {
                        CGPoint p = [x CGPointValue];
                        
                        if (p.y <= 0 && p.y >= - 64) {
                            self.fakeBackgroundView.layer.shadowOpacity = fabs(64 + p.y) / 64 * 0.7;
                        }
                        else if (p.y > 0)
                        {
                            if (self.fakeBackgroundView.layer.shadowOpacity != 0.7) {
                                self.fakeBackgroundView.layer.shadowOpacity = 0.7;
                            }
                        }
                        else
                        {
                            if (self.fakeBackgroundView.layer.shadowOpacity != 0) {
                                self.fakeBackgroundView.layer.shadowOpacity = 0;
                            }
                        }
                    }];
                }
            }];
        }
    }];
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,229評論 4 61
  • 一.項目引入依賴 在開始之前,首先給出GitHub上的鏈接 Githubhttps://github.com/Re...
    藍楓zeke閱讀 4,202評論 0 8
  • 我去到一個地方 到處是黑色的花 氣球都垂在地上 石頭都浮在半空 所有人只能倒立行走 臉貼地面,腳戴紅花 ...
    左小事界閱讀 134評論 0 0
  • 目錄 (五)初升的太陽 在連續四周奔波于公司和項目地之后,她終于病倒了。作為新官上任,剛接手了兩個新項目,出于自律...
    我從彩虹那邊來閱讀 413評論 0 1
  • 大螃蟹 ▲ 21周歲了,沒聽過很多的歌,總在向往詩和遠方,眼前算不上茍且,平靜的生活只道是尋常阡陌。 很多的時候自...
    螃蟹螃蟹閱讀 440評論 5 1