A.NavigationBar标题按钮
1.需求
- 在“首页”的导航栏中部设置一个“首页”文字+箭头按钮
- 统一设置样式
- 根据实际文本长度调整宽度
- 消除系统自带的点击高亮效果
- 点击按钮,箭头上下颠倒
2.思路
- 使用UIButton,设置文本和图片
- 在initWithFrame统一样式
- 创建一个继承UIButton的自定义类,重写文本和图片的绘图方法,互换位置
- 设置一个标识成员变量,判断当前的按钮状态(弹出 or 缩回)
3.实现
(1)自定义继承UIButton的类 HVWNavigationBarTitleButton
1 // 2 // HVWNavigationBarTitleButton.m 3 // HVWWeibo 4 // 5 // Created by hellovoidworld on 15/2/2. 6 // Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8 9 #import "HVWNavigationBarTitleButton.h" 10 11 @implementation HVWNavigationBarTitleButton 12 13 /** 重写initWithFrame, 统一设置按钮的样式 */ 14 - (instancetype)initWithFrame:(CGRect)frame { 15 if (self = [super initWithFrame:frame]) { 16 // 设置字体 17 self.titleLabel.font = HVWNavigationTitleFont; 18 [self setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; 19 20 // 文本右对齐 21 [self.titleLabel setTextAlignment:NSTextAlignmentRight]; 22 23 // 取消图标高亮效果 24 [self setAdjustsImageWhenDisabled:NO]; 25 26 // 图片居中 27 [self.imageView setContentMode:UIViewContentModeCenter]; 28 29 } 30 31 return self; 32 } 33 34 /** 重写iamge的绘图方法 */ 35 - (CGRect)imageRectForContentRect:(CGRect)contentRect { 36 CGFloat height = contentRect.size.height; 37 CGFloat width = height; 38 CGFloat x = self.size.width - width; 39 CGFloat y = 0; 40 return CGRectMake(x, y, width, height); 41 } 42 43 /** 重写title的绘图方法 */ 44 - (CGRect)titleRectForContentRect:(CGRect)contentRect { 45 CGFloat height = contentRect.size.height; 46 // 文本宽度 = 按钮整体宽度 - 图片宽度 47 CGFloat width = self.height - height; 48 CGFloat x = 0; 49 CGFloat y = 0; 50 return CGRectMake(x, y, width, height); 51 } 52 53 @end
(2)在“首页”控制器设置“标题按钮”:
HVWHomeViewController.m:
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 4 // 添加导航控制器按钮 5 // 左边按钮 6 self.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImage:@"navigationbar_friendsearch" hightlightedImage:@"navigationbar_friendsearch_highlighted" target:self selector:@selector(searchFriend)]; 7 8 // 右边按钮 9 self.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImage:@"navigationbar_pop" hightlightedImage:@"navigationbar_pop_highlighted" target:self selector:@selector(pop)]; 10 11 // 设置标题按钮 12 HVWNavigationBarTitleButton *titleButton = [[HVWNavigationBarTitleButton alloc] init]; 13 titleButton.height = 35; 14 titleButton.width = 100; 15 [titleButton setTitle:@"首页" forState:UIControlStateNormal]; 16 [titleButton setImage:[UIImage imageWithNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 17 // 设置背景图片 18 [titleButton setBackgroundImage:[UIImage resizedImage:@"navigationbar_filter_background_highlighted"] forState:UIControlStateHighlighted]; 19 20 // 监听按钮点击事件,替换图标 21 [titleButton addTarget:self action:@selector(titleButtonClickd:) forControlEvents:UIControlEventTouchUpInside]; 22 23 self.navigationItem.titleView = titleButton; 24 } 25 26 /** 标题栏按钮点击事件 */ 27 - (void) titleButtonClickd:(UIButton *) button { 28 self.titleButtonExtended = !self.titleButtonExtended; 29 30 if (self.isTitleButtonExtended) { 31 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_up"] forState:UIControlStateNormal]; 32 } else { 33 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 34 } 35 } 36 37 #mark:有希望依赖图片缓存,使用"=="判断当前按钮图片来决定按钮状态的,发现使用currentImage和新建一个UIImage(同一张图片)出来的地址并不一致!所以不能采用。 38 -(void)titleButtonClick:(UIButton *)titleButton 39 { 40 UIImage *titleImage=[UIImage imageWithName:@"navigationbar_arrow_down"]; 41 42 if (titleButton.currentImage==titleImage) { 43 //换成箭头向上 44 [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_up"] forState:UIControlStateNormal]; 45 }else 46 { 47 //换成箭头向下 48 [titleButton setImage:[UIImage imageWithName:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 49 } 50 }
B.导航栏标题按钮弹出框
1.需求
点击导航栏标题按钮弹出一个框
点击框外的其他地方,隐藏此框
2.思路
因为框的范围涉及到了导航栏,所以不能放在导航栏下的内容界面控制器上,要放在导航栏上
在框和导航栏的夹层放置一个全屏的(透明/半透明)的UIButton,用来监听框外点击
封装此功能,可以作为一个弹出菜单控件
(1)简单尝试直接在窗口上添加一个UIImageView
HVWHomeViewController:
1 /** 标题栏按钮点击事件 */ 2 - (void) titleButtonClickd:(UIButton *) button { 3 self.titleButtonExtended = !self.titleButtonExtended; 4 5 if (self.isTitleButtonExtended) { 6 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_up"] forState:UIControlStateNormal]; 7 8 // 弹出框 9 UIView *window = [[UIApplication sharedApplication] keyWindow]; 10 UIImageView *popView = [[UIImageView alloc] init]; 11 popView.size = CGSizeMake(200, 200); 12 popView.centerX = window.width * 0.5; 13 popView.y = 55; 14 popView.image = [UIImage resizedImage:@"popover_background"]; 15 [self.navigationController.view addSubview:popView]; 16 17 } else { 18 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 19 } 20 }
(2)点击其他区域,隐藏弹出框
使用一个全屏透明/半透明UIButton夹在弹出框和底层的控件之间,监听点击事件
1 /** 标题栏按钮点击事件 */ 2 - (void) titleButtonClickd:(UIButton *) button { 3 self.titleButtonExtended = !self.titleButtonExtended; 4 5 if (self.isTitleButtonExtended) { 6 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_up"] forState:UIControlStateNormal]; 7 8 UIView *window = [[UIApplication sharedApplication] keyWindow]; 9 10 // 中间辅助覆盖层(帮助隐藏弹出框) 11 UIButton *coverLayer = [UIButton buttonWithType:UIButtonTypeCustom]; 12 coverLayer.frame = window.bounds; 13 coverLayer.backgroundColor = [UIColor blackColor]; 14 coverLayer.alpha = 0.2; 15 [window addSubview:coverLayer]; 16 [coverLayer addTarget:self action:@selector(coverLayerClicked:) forControlEvents:UIControlEventTouchUpInside]; 17 18 // 弹出框 19 UIImageView *popView = [[UIImageView alloc] init]; 20 self.popView = popView; 21 popView.size = CGSizeMake(200, 200); 22 popView.centerX = window.width * 0.5; 23 popView.y = 55; 24 popView.userInteractionEnabled = YES; 25 26 popView.image = [UIImage resizedImage:@"popover_background"]; 27 [window addSubview:popView]; 28 29 } else { 30 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 31 } 32 } 33 34 /** 辅助覆盖层点击事件 */ 35 - (void) coverLayerClicked:(UIButton *) button { 36 if (self.isTitleButtonExtended) { 37 [button removeFromSuperview]; 38 [self.popView removeFromSuperview]; 39 [self titleButtonClickd:self.titleButton]; 40 } 41 }
(3)由于弹出框功能可能在多处用到,而且让控制器负责创建不合适,所以封装成一个类
封装成“弹出菜单”类HVWPopMenu(继承UIView)
内部包含:
背景图片
遮盖UIButton
一个内容界面 (如tableViewController),作为背景图片的子控件
1 // 2 // HVWPopMenu.h 3 // HVWWeibo 4 // 5 // Created by hellovoidworld on 15/2/2. 6 // Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8 9 #import <UIKit/UIKit.h> 10 11 typedef enum { 12 PopMenuArrowLeft, 13 PopMenuArrowMid, 14 PopMenuArrowRight 15 } PopMenuArrow; 16 17 @class HVWPopMenu; 18 @protocol HVWPopMenuDelegate <NSObject> 19 20 @optional 21 - (void) popMenuDidHideMenu:(HVWPopMenu *) popMenu; 22 23 @end 24 25 @interface HVWPopMenu : UIView 26 27 /** 背景兼内容容器 */ 28 @property(nonatomic, strong) UIImageView *backgroundContainer; 29 30 #pragma mark - 成员属性 31 /** 遮盖夹层 */ 32 @property(nonatomic, strong) UIButton *coverLayer; 33 34 /** 内容控件 */ 35 @property(nonatomic, strong) UIView *contentView; 36 37 /** 箭头位置 */ 38 @property(nonatomic, assign) PopMenuArrow popMenuArrow; 39 40 /** 遮盖夹层透明标识 */ 41 @property(nonatomic, assign, getter=isDimCoverLayer) BOOL dimCoverLayer; 42 43 /** 代理 */ 44 @property(nonatomic, weak) id<HVWPopMenuDelegate> delegate; 45 46 47 #pragma mark - 初始化方法 48 - (instancetype) initWithContentView:(UIView *) contentView; 49 + (instancetype) popMenuWithContentView:(UIView *) contentView; 50 51 #pragma mark - 使用方法 52 /** 弹出 */ 53 - (void) showMenuInRect:(CGRect) rect; 54 55 /** 隐藏 */ 56 - (void) hideMenu; 57 58 59 @end
1 // 2 // HVWPopMenu.m 3 // HVWWeibo 4 // 5 // Created by hellovoidworld on 15/2/2. 6 // Copyright (c) 2015年 hellovoidworld. All rights reserved. 7 // 8 9 #import "HVWPopMenu.h" 10 11 @implementation HVWPopMenu 12 13 #pragma mark - 初始化方法 14 - (instancetype) initWithContentView:(UIView *) contentView { 15 if (self = [super init]) { 16 self.contentView = contentView; 17 } 18 19 return self; 20 } 21 22 + (instancetype) popMenuWithContentView:(UIView *) contentView { 23 return [[self alloc] initWithContentView:contentView]; 24 } 25 26 /** 初始化子控件 */ 27 - (instancetype)initWithFrame:(CGRect)frame { 28 self = [super initWithFrame:frame]; 29 if (self) { 30 // 中间辅助覆盖层(帮助隐藏弹出框) 31 UIButton *coverLayer = [UIButton buttonWithType:UIButtonTypeCustom]; 32 self.coverLayer = coverLayer; 33 [self setDimCoverLayer:YES]; 34 [coverLayer addTarget:self action:@selector(coverLayerClicked) forControlEvents:UIControlEventTouchUpInside]; 35 [self addSubview:coverLayer]; 36 37 // 添加背景容器 38 UIImageView *backgroundContainer = [[UIImageView alloc] init]; 39 self.backgroundContainer = backgroundContainer; 40 backgroundContainer.userInteractionEnabled = YES; 41 [self setPopMenuArrow:PopMenuArrowMid]; 42 [self addSubview:backgroundContainer]; 43 } 44 45 return self; 46 } 47 48 /** 遮盖夹层点击事件 */ 49 - (void) coverLayerClicked { 50 [self hideMenu]; 51 } 52 53 #pragma mark - 使用方法 54 /** 弹出 */ 55 - (void) showMenuInRect:(CGRect) rect { 56 // 准备添加到当前主窗口上 57 UIView *window = [[UIApplication sharedApplication] keyWindow]; 58 self.frame = window.bounds; 59 [window addSubview:self]; 60 61 self.coverLayer.frame = window.bounds; 62 self.backgroundContainer.frame = rect; 63 64 // 添加内容控件 65 if (self.contentView) { 66 CGFloat topMargin = 12; 67 CGFloat leftMargin = 5; 68 CGFloat bottomMargin = 8; 69 CGFloat rightMargin = 5; 70 71 self.contentView.x = leftMargin; 72 self.contentView.y = topMargin; 73 self.contentView.width = self.backgroundContainer.width - leftMargin - rightMargin; 74 self.contentView.height = self.backgroundContainer.height - topMargin - bottomMargin; 75 76 [self.backgroundContainer addSubview:self.contentView]; 77 } 78 } 79 80 /** 隐藏 */ 81 - (void) hideMenu { 82 if ([self.delegate respondsToSelector:@selector(popMenuDidHideMenu:)]) { 83 [self.delegate popMenuDidHideMenu:self]; 84 } 85 86 [self removeFromSuperview]; 87 } 88 89 #pragma mark - 特性设置 90 /** 设置遮盖夹层是否透明 */ 91 - (void)setDimCoverLayer:(BOOL)dimCoverLayer { 92 if (dimCoverLayer) { // 需要半透明模糊效果的 93 self.coverLayer.backgroundColor = [UIColor blackColor]; 94 self.coverLayer.alpha = 0.2; 95 } else { // 全透明 96 self.coverLayer.backgroundColor = [UIColor clearColor]; 97 self.coverLayer.alpha = 1.0; 98 } 99 } 100 101 102 /** 设置弹出菜单顶部箭头位置:左、中、右 */ 103 - (void)setPopMenuArrow:(PopMenuArrow)popMenuArrow { 104 _popMenuArrow = popMenuArrow; 105 106 switch (popMenuArrow) { 107 case PopMenuArrowLeft: 108 self.backgroundContainer.image = [UIImage resizedImage:@"popover_background_left"]; 109 break; 110 case PopMenuArrowMid: 111 self.backgroundContainer.image = [UIImage resizedImage:@"popover_background"]; 112 break; 113 case PopMenuArrowRight: 114 self.backgroundContainer.image = [UIImage resizedImage:@"popover_background_right"]; 115 break; 116 default: 117 break; 118 } 119 } 120 121 @end
1 // HVWHomeViewController.m 2 /** 标题栏按钮点击事件 */ 3 - (void) titleButtonClickd:(UIButton *) button { 4 self.titleButtonExtended = !self.titleButtonExtended; 5 6 if (self.isTitleButtonExtended) { 7 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_up"] forState:UIControlStateNormal]; 8 9 // 添加弹出菜单 10 UITableView *tableView = [[UITableView alloc] init]; 11 HVWPopMenu *popMenu = [HVWPopMenu popMenuWithContentView:tableView]; 12 popMenu.delegate = self; 13 popMenu.dimCoverLayer = YES; // 模糊遮盖 14 popMenu.popMenuArrow = PopMenuArrowMid; // 中部箭头 15 16 // 弹出 17 [popMenu showMenuInRect:CGRectMake(50, 55, 200, 300)]; 18 19 } else { 20 [button setImage:[UIImage imageWithNamed:@"navigationbar_arrow_down"] forState:UIControlStateNormal]; 21 } 22 } 23 24 #pragma mark - HVWPopMenuDelegate 25 - (void)popMenuDidHideMenu:(HVWPopMenu *)popMenu { 26 UIButton *titleButton = (UIButton *)self.navigationItem.titleView; 27 [self titleButtonClickd:titleButton]; 28 }