博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UINavigationBar的继承与定制
阅读量:6958 次
发布时间:2019-06-27

本文共 6611 字,大约阅读时间需要 22 分钟。

我们在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实例修改默认值,是不是很炫酷。

项目需求

上面一段与本文正题无关,下面我们看一下本文的项目需求

图片描述

分析

这个页面就是一个标准的NavigationController + TableViewContoller组合实现的设置页面,导航条和Table的样式需要订制。

前面说到的UIApperance协议是可以实现的,我们换一种更为普遍的方式实现,继承

我们继承UINavigationBar,创建子类FWBar。我们使用storyboard实例化大体框架模型,并将NavigationViewControllerNavigationBar设置为我们的FWBar类,并将UITableView设置为Static静态模式,直接编辑了Cell的内容。

图片描述

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. 这一段使用了特殊的语法糖,有兴趣了解的参考,全文搜索关键字小括号内联复合表达式

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

最后的实现效果。

图片描述

结语

截屏的效果不是太好,细心的朋友可能会发现,我们的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;                            }                        }                    }];                }            }];        }    }];}

转载地址:http://ewqil.baihongyu.com/

你可能感兴趣的文章
itext处理pdf
查看>>
POJ 1276 Cash Machine
查看>>
Oracle EBS Color 色彩设置
查看>>
C语言中 struct成员变量顺序对内存的占用
查看>>
POJ1291-并查集/dfs
查看>>
广州项目实施步骤II_练习配置HaProxy的重定向负载均衡
查看>>
INNO SETUP 5.5.0以上版本中文语言包
查看>>
新旧资源库访问地址
查看>>
C#秘密武器之扩展方法
查看>>
Linux2.6内核实现的是NPTL
查看>>
windows系统上安装与使用Android NDK r8d(一)
查看>>
poj 1950 Dessert(dfs枚举,模拟运算过程)
查看>>
Ubuntu Server对OpenStack的支持
查看>>
win7下设置环境变量
查看>>
那些年我们一起追过的缓存写法(四)
查看>>
Node.js 0.12: 正确发送HTTP POST请求
查看>>
Photoshop如何实现UI自动切图?
查看>>
《算法导论》读书笔记之第16章 0-1背包问题—动态规划求解
查看>>
Html5_移动前端不得不了解的html5 head 头标签
查看>>
UVA12304 2D Geometry 110 in 1! 计算几何
查看>>