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;
                            }
                        }
                    }];
                }
            }];
        }
    }];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,924评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,902评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,716评论 0 239
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,783评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,166评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,510评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,784评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,476评论 0 196
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,196评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,459评论 2 243
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,978评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,321评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,964评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,046评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,803评论 0 193
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,530评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,420评论 2 265

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,609评论 4 59
  • 一.项目引入依赖 在开始之前,首先给出GitHub上的链接 Githubhttps://github.com/Re...
    蓝枫zeke阅读 4,118评论 0 8
  • 我去到一个地方 到处是黑色的花 气球都垂在地上 石头都浮在半空 所有人只能倒立行走 脸贴地面,脚戴红花 ...
    左小事界阅读 112评论 0 0
  • 目录 (五)初升的太阳 在连续四周奔波于公司和项目地之后,她终于病倒了。作为新官上任,刚接手了两个新项目,出于自律...
    我从彩虹那边来阅读 375评论 0 1
  • 大螃蟹 ▲ 21周岁了,没听过很多的歌,总在向往诗和远方,眼前算不上苟且,平静的生活只道是寻常阡陌。 很多的时候自...
    螃蟹螃蟹阅读 387评论 5 1