一:主框架分析:
1:主框架的效果图如下:底部有一个tabBar,点击tabBar按钮切换首页,信息,发现 和我几个界面。则考虑将UITabBarController作为窗口的根视图控制器,所以新建类继承UITabBarController,用来封装主框架
:
2:封装根视图控制器UITabBarController的代码:
1 #import "HMTabBarViewController.h"
2 #import "HMHomeViewController.h"
3 #import "HMMessageViewController.h"
4 #import "HMDiscoverViewController.h"
5 #import "HMProfileViewController.h"
6 #import "HMNavigationController.h"
7 #import "HMTabBar.h"
8 #import "HMComposeViewController.h"
9 #import "HMUserTool.h"
10 #import "HMAccount.h"
11 #import "HMAccountTool.h"
12
13 @interface HMTabBarViewController () <HMTabBarDelegate, UITabBarControllerDelegate>
14 @property (nonatomic, weak) HMHomeViewController *home;
15 @property (nonatomic, weak) HMMessageViewController *message;
16 @property (nonatomic, weak) HMProfileViewController *profile;
17 @property (nonatomic, weak) UIViewController *lastSelectedViewContoller;
18 @end
19
20 @implementation HMTabBarViewController
21 - (void)viewDidLoad
22 {
23 [super viewDidLoad];
24
25 //1:自己设为自己的代理,为了监听自身的改变
26 self.delegate = self;
27
28 //2:添加所有的子控制器
29 [self addAllChildVcs];
30
31 //3:创建自定义tabbar
32 [self addCustomTabBar];
33
34 //4:利用定时器获得用户的未读数
35 // NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(getUnreadCount) userInfo:nil repeats:YES];
36 // [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
37 }
38
39 #pragma mark -- 当切换控制器时,监听自身切换控制器
40
41 - (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UINavigationController *)viewController
42 {
43 UIViewController *vc = [viewController.viewControllers firstObject];
44 if ([vc isKindOfClass:[HMHomeViewController class]]) {
45 if (self.lastSelectedViewContoller == vc) {
46 [self.home refresh:YES];
47 } else {
48 [self.home refresh:NO];
49 }
50 }
51
52 self.lastSelectedViewContoller = vc;
53 }
54
55 #pragma mark -- 获得未读数
56
57 - (void)getUnreadCount
58 {
59 // 1.请求参数
60 HMUnreadCountParam *param = [HMUnreadCountParam param];
61 param.uid = [HMAccountTool account].uid;
62
63 // 2.获得未读数
64 [HMUserTool unreadCountWithParam:param success:^(HMUnreadCountResult *result) {
65 // 显示微博未读数
66 if (result.status == 0) {
67 self.home.tabBarItem.badgeValue = nil;
68 } else {
69 self.home.tabBarItem.badgeValue = [NSString stringWithFormat:@"%d", result.status];
70 }
71
72 // 显示消息未读数
73 if (result.messageCount == 0) {
74 self.message.tabBarItem.badgeValue = nil;
75 } else {
76 self.message.tabBarItem.badgeValue = [NSString stringWithFormat:@"%d", result.messageCount];
77 }
78
79 // 显示新粉丝数
80 if (result.follower == 0) {
81 self.profile.tabBarItem.badgeValue = nil;
82 } else {
83 self.profile.tabBarItem.badgeValue = [NSString stringWithFormat:@"%d", result.follower];
84 }
85
86 // 在图标上显示所有的未读数
87 [UIApplication sharedApplication].applicationIconBadgeNumber = result.totalCount;
88 HMLog(@"总未读数--%d", result.totalCount);
89 } failure:^(NSError *error) {
90 HMLog(@"获得未读数失败---%@", error);
91 }];
92 }
93
94 /**
95 * 创建自定义tabbar
96 */
97 - (void)addCustomTabBar
98 {
99 // 创建自定义tabbar
100 HMTabBar *customTabBar = [[HMTabBar alloc] init];
101 customTabBar.tabBarDelegate = self;
102 // 更换系统自带的tabbar
103 [self setValue:customTabBar forKeyPath:@"tabBar"];
104 }
105
106 /**
107 * 添加所有的子控制器
108 */
109 - (void)addAllChildVcs
110 {
111 HMHomeViewController *home = [[HMHomeViewController alloc] init];
112 [self addOneChlildVc:home title:@"首页" imageName:@"tabbar_home" selectedImageName:@"tabbar_home_selected"];
113 self.home = home;
114 self.lastSelectedViewContoller = home;
115
116 HMMessageViewController *message = [[HMMessageViewController alloc] init];
117 [self addOneChlildVc:message title:@"消息" imageName:@"tabbar_message_center" selectedImageName:@"tabbar_message_center_selected"];
118 self.message = message;
119
120 HMDiscoverViewController *discover = [[HMDiscoverViewController alloc] init];
121 [self addOneChlildVc:discover title:@"发现" imageName:@"tabbar_discover" selectedImageName:@"tabbar_discover_selected"];
122
123 HMProfileViewController *profile = [[HMProfileViewController alloc] init];
124 [self addOneChlildVc:profile title:@"我" imageName:@"tabbar_profile" selectedImageName:@"tabbar_profile_selected"];
125 self.profile = profile;
126 }
127
128 /**
129 * 添加一个子控制器
130 *
131 * @param childVc 子控制器对象
132 * @param title 标题
133 * @param imageName 图标
134 * @param selectedImageName 选中的图标
135 */
136 - (void)addOneChlildVc:(UIViewController *)childVc title:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName
137 {
138 // 设置标题
139 childVc.title = title;
140
141 // 设置图标
142 childVc.tabBarItem.image = [UIImage imageWithName:imageName];
143
144 // 设置tabBarItem的普通文字颜色
145 NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
146 textAttrs[NSForegroundColorAttributeName] = [UIColor blackColor];
147 textAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:10];
148 [childVc.tabBarItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
149
150 // 设置tabBarItem的选中文字颜色
151 NSMutableDictionary *selectedTextAttrs = [NSMutableDictionary dictionary];
152 selectedTextAttrs[NSForegroundColorAttributeName] = [UIColor orangeColor];
153 [childVc.tabBarItem setTitleTextAttributes:selectedTextAttrs forState:UIControlStateSelected];
154
155 // 设置选中的图标
156 UIImage *selectedImage = [UIImage imageWithName:selectedImageName];
157 if (iOS7) {
158 // 声明这张图片用原图(别渲染)
159 selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
160 }
161 childVc.tabBarItem.selectedImage = selectedImage;
162
163 // 添加为tabbar控制器的子控制器
164 HMNavigationController *nav = [[HMNavigationController alloc] initWithRootViewController:childVc];
165 [self addChildViewController:nav];
166 }
167
168 #pragma mark - HMTabBarDelegate
169 - (void)tabBarDidClickedPlusButton:(HMTabBar *)tabBar
170 {
171 // 弹出发微博控制器
172 HMComposeViewController *compose = [[HMComposeViewController alloc] init];
173 HMNavigationController *nav = [[HMNavigationController alloc] initWithRootViewController:compose];
174 [self presentViewController:nav animated:YES completion:nil];
175 }
176 @end
3:封装导航控制器的代码:
#import <UIKit/UIKit.h>
@interface HMNavigationController : UINavigationController
@end
#import "HMNavigationController.h"
@interface HMNavigationController ()
@end
@implementation HMNavigationController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
/**
* 当导航控制器的view创建完毕就调用
*/
- (void)viewDidLoad
{
[super viewDidLoad];
// 清空弹出手势的代理,就可以恢复弹出手势
self.interactivePopGestureRecognizer.delegate = nil;
}
/**
* 当第一次使用这个类的时候调用1次
*/
+ (void)initialize
{
// 设置UINavigationBarTheme的主
[self setupNavigationBarTheme];
// 设置UIBarButtonItem的主题
[self setupBarButtonItemTheme];
}
/**
* 设置UINavigationBarTheme的主题
*/
+ (void)setupNavigationBarTheme
{
UINavigationBar *appearance = [UINavigationBar appearance];
// 设置导航栏背景
if (!iOS7) {
[appearance setBackgroundImage:[UIImage imageWithName:@"navigationbar_background"] forBarMetrics:UIBarMetricsDefault];
}
// 设置文字属性
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
textAttrs[UITextAttributeTextColor] = [UIColor blackColor];
// UITextAttributeFont --> NSFontAttributeName(iOS7)
#warning 过期 : 并不代表不能用, 仅仅是有最新的方案可以取代它
textAttrs[UITextAttributeFont] = HMNavigationTitleFont;
// UIOffsetZero是结构体, 只要包装成NSValue对象, 才能放进字典\数组中
textAttrs[UITextAttributeTextShadowOffset] = [NSValue valueWithUIOffset:UIOffsetZero];
[appearance setTitleTextAttributes:textAttrs];
}
/**
* 设置UIBarButtonItem的主题
*/
+ (void)setupBarButtonItemTheme
{
// 通过appearance对象能修改整个项目中所有UIBarButtonItem的样式
UIBarButtonItem *appearance = [UIBarButtonItem appearance];
/**设置文字属性**/
// 设置普通状态的文字属性
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
textAttrs[UITextAttributeTextColor] = [UIColor orangeColor];
textAttrs[UITextAttributeFont] = [UIFont systemFontOfSize:15];
textAttrs[UITextAttributeTextShadowOffset] = [NSValue valueWithUIOffset:UIOffsetZero];
[appearance setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
// 设置高亮状态的文字属性
NSMutableDictionary *highTextAttrs = [NSMutableDictionary dictionaryWithDictionary:textAttrs];
highTextAttrs[UITextAttributeTextColor] = [UIColor redColor];
[appearance setTitleTextAttributes:highTextAttrs forState:UIControlStateHighlighted];
// 设置不可用状态(disable)的文字属性
NSMutableDictionary *disableTextAttrs = [NSMutableDictionary dictionaryWithDictionary:textAttrs];
disableTextAttrs[UITextAttributeTextColor] = [UIColor lightGrayColor];
[appearance setTitleTextAttributes:disableTextAttrs forState:UIControlStateDisabled];
/**设置背景**/
// 技巧: 为了让某个按钮的背景消失, 可以设置一张完全透明的背景图片
[appearance setBackgroundImage:[UIImage imageWithName:@"navigationbar_button_background"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
}
/**
* 能拦截所有push进来的子控制器
*/
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (self.viewControllers.count > 0) { // 如果现在push的不是栈底控制器(最先push进来的那个控制器)
viewController.hidesBottomBarWhenPushed = YES;
// 设置导航栏按钮
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_back" highImageName:@"navigationbar_back_highlighted" target:self action:@selector(back)];
viewController.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_more" highImageName:@"navigationbar_more_highlighted" target:self action:@selector(more)];
}
[super pushViewController:viewController animated:animated];
}
- (void)back
{
#warning 这里用的是self, 因为self就是当前正在使用的导航控制器
[self popViewControllerAnimated:YES];
}
- (void)more
{
[self popToRootViewControllerAnimated:YES];
}
@end
4:封装tabBar代码:
#import <UIKit/UIKit.h>
@class HMTabBar;
@protocol HMTabBarDelegate <NSObject>
@optional
- (void)tabBarDidClickedPlusButton:(HMTabBar *)tabBar;
@end
@interface HMTabBar : UITabBar
@property (nonatomic, weak) id<HMTabBarDelegate> tabBarDelegate;
@end
#import "HMTabBar.h"
@interface HMTabBar()
@property (nonatomic, weak) UIButton *plusButton;
@end
@implementation HMTabBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
if (!iOS7) {
self.backgroundImage = [UIImage imageWithName:@"tabbar_background"];
}
self.selectionIndicatorImage = [UIImage imageWithName:@"navigationbar_button_background"];
// 添加加号按钮
[self setupPlusButton];
}
return self;
}
/**
* 添加加号按钮
*/
- (void)setupPlusButton
{
UIButton *plusButton = [[UIButton alloc] init];
// 设置背景
[plusButton setBackgroundImage:[UIImage imageWithName:@"tabbar_compose_button"] forState:UIControlStateNormal];
[plusButton setBackgroundImage:[UIImage imageWithName:@"tabbar_compose_button_highlighted"] forState:UIControlStateHighlighted];
// 设置图标
[plusButton setImage:[UIImage imageWithName:@"tabbar_compose_icon_add"] forState:UIControlStateNormal];
[plusButton setImage:[UIImage imageWithName:@"tabbar_compose_icon_add_highlighted"] forState:UIControlStateHighlighted];
[plusButton addTarget:self action:@selector(plusClick) forControlEvents:UIControlEventTouchUpInside];
// 添加
[self addSubview:plusButton];
self.plusButton = plusButton;
}
- (void)plusClick
{
HMLog(@"plusClick----");
// 通知代理
if ([self.tabBarDelegate respondsToSelector:@selector(tabBarDidClickedPlusButton:)]) {
[self.tabBarDelegate tabBarDidClickedPlusButton:self];
}
}
/**
* 布局子控件
*/
- (void)layoutSubviews
{
[super layoutSubviews];
// 设置plusButton的frame
[self setupPlusButtonFrame];
// 设置所有tabbarButton的frame
[self setupAllTabBarButtonsFrame];
}
/**
* 设置所有plusButton的frame
*/
- (void)setupPlusButtonFrame
{
self.plusButton.size = self.plusButton.currentBackgroundImage.size;
self.plusButton.center = CGPointMake(self.width * 0.5, self.height * 0.5);
}
/**
* 设置所有tabbarButton的frame
*/
- (void)setupAllTabBarButtonsFrame
{
int index = 0;
// 遍历所有的button
for (UIView *tabBarButton in self.subviews) {
// 如果不是UITabBarButton, 直接跳过
if (![tabBarButton isKindOfClass:NSClassFromString(@"UITabBarButton")]) continue;
// 根据索引调整位置
[self setupTabBarButtonFrame:tabBarButton atIndex:index];
// 索引增加
index++;
}
}
/**
* 设置某个按钮的frame
*
* @param tabBarButton 需要设置的按钮
* @param index 按钮所在的索引
*/
- (void)setupTabBarButtonFrame:(UIView *)tabBarButton atIndex:(int)index
{
// 计算button的尺寸
CGFloat buttonW = self.width / (self.items.count + 1);
CGFloat buttonH = self.height;
tabBarButton.width = buttonW;
tabBarButton.height = buttonH;
if (index >= 2) {
tabBarButton.x = buttonW * (index + 1);
} else {
tabBarButton.x = buttonW * index;
}
tabBarButton.y = 0;
}
@end
5:封装UIBarButtonItem:分类封装
//自定义图片按钮:常态,高亮状态
#import <UIKit/UIKit.h>
@interface UIBarButtonItem (Extension)
+ (UIBarButtonItem *)itemWithImageName:(NSString *)imageName highImageName:(NSString *)highImageName target:(id)target action:(SEL)action;
@end
#import "UIBarButtonItem+Extension.h"
@implementation UIBarButtonItem (Extension)
+ (UIBarButtonItem *)itemWithImageName:(NSString *)imageName highImageName:(NSString *)highImageName target:(id)target action:(SEL)action
{
UIButton *button = [[UIButton alloc] init];
[button setBackgroundImage:[UIImage imageWithName:imageName] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageWithName:highImageName] forState:UIControlStateHighlighted];
// 设置按钮的尺寸为背景图片的尺寸
button.size = button.currentBackgroundImage.size;
// 监听按钮点击
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
return [[UIBarButtonItem alloc] initWithCustomView:button];
}
@end
6:封装UIView的frame:分类封装
#import <UIKit/UIKit.h>
@interface UIView (Extension)
@property (nonatomic, assign) CGFloat x;
@property (nonatomic, assign) CGFloat y;
@property (nonatomic, assign) CGFloat centerX;
@property (nonatomic, assign) CGFloat centerY;
@property (nonatomic, assign) CGFloat width;
@property (nonatomic, assign) CGFloat height;
@property (nonatomic, assign) CGSize size;
@end
#import "UIView+Extension.h"
@implementation UIView (Extension)
- (void)setX:(CGFloat)x
{
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)x
{
return self.frame.origin.x;
}
- (void)setY:(CGFloat)y
{
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)y
{
return self.frame.origin.y;
}
- (void)setCenterX:(CGFloat)centerX
{
CGPoint center = self.center;
center.x = centerX;
self.center = center;
}
- (CGFloat)centerX
{
return self.center.x;
}
- (void)setCenterY:(CGFloat)centerY
{
CGPoint center = self.center;
center.y = centerY;
self.center = center;
}
- (CGFloat)centerY
{
return self.center.y;
}
- (void)setWidth:(CGFloat)width
{
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)width
{
return self.frame.size.width;
}
- (void)setHeight:(CGFloat)height
{
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)height
{
return self.frame.size.height;
}
- (void)setSize:(CGSize)size
{
// self.width = size.width;
// self.height = size.height;
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGSize)size
{
return self.frame.size;
}
@end
三:思路分析:
1:先添加首页,信息,发现,我四个控制器为根视图控制器的子控制器,在viewDidload方法里封装方法[self addAllChildVcs];调用。因为四个子控制器中每个子控制器都要设置title,Vc.tabBarItem.image,Vc.abBarItem.selectedImage,以及abBarItem的文字属性,还要包装一层导航控制器,最后addChildViewController作为根视图控制器的子控制器。此时会有大量重复的代码,所以要想抽取代码的思想,将大量重复的代码抽成一个方法,把相同的代码封装在方法的内部,不同的部分作为参数传递。所以将此段代码抽成一个方法:- (void)addOneChlildVc:(UIViewController *)childVc title:(NSString *)title imageName:(NSString *)imageName selectedImageName:(NSString *)selectedImageName;创建出子控制器对象后,将子控制器对象,title,image ,selectedImage作为参数传递。
2:设置tabItem的文字属性:1:在封装方法中,设置标题属性, childVc.title = title;此句代码既设置了tabBaritem上的标题,也设置了相应子控制器的导航栏的标题。设置tabBar上不同状态下的标题的文字属性:利用方法 setTitleTextAttributes:dic forState:state 就可以设置不同状态下的文字属性,常态,高亮,失效。(给字典赋值:dic[key]= value;dic为可变字典)
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
textAttrs[NSForegroundColorAttributeName] = [UIColor blackColor];
textAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:10];
[childVc.tabBarItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
3:设置tabItem上不同状态下的图片属性:1:Normal :childVc.tabBarItem.image = [UIImage imageWithName:imageName]; 2:selected: UIImage *selectedImage = [UIImage imageWithName:selectedImageName];
if (iOS7) {
// 声明这张图片用原图(别渲染) selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; }
childVc.tabBarItem.selectedImage = selectedImage;
1:判断是否是ios7,可以在pch文件中做宏定义,其中一些宏定义,一些头文件都可以在pch中做定义。
#define iOS7 ([[UIDevice currentDevice].systemVersion doubleValue] >= 7.0)
2:对于NSLog的宏定义也可以在pch中:
1 #ifdef DEBUG //调式状态
2 #define DLog(...) NSLog (__VA_ARGS__)
3 #else //发布状态
4 #define DLog (...)
5 #endif
3:单例的宏定义:
//单例的宏定义:斜线向右,表示下一行也属于宏定义
#define Single_interface(className) +(className*)shared##className
#define Single_implemtation(className) +(className*)shared##className { \
\
static className *instance = nil;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
\
instance = [[self alloc]init];\
});\
\
return instance;\
}
4:在系统大于或等于ios7时,系统默认会对系统的一些控件产生渲染,渲染为蓝色,例如button为系统样式时,会被渲染为蓝色,而tabBarItem上的图片,normal状态下不会被渲染,只有是选中selected状态下才会被渲染成蓝色。解决办法:三部曲:1:先创建图片 2:判断是否为ios7若是则调用UIImage的方法imageWithRenderingMode,告诉系统不要渲染,保持原图,并返回一张原图的图片。selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 3:设置tabBarietm上的选中图片
UIImage *selectedImage = [UIImage imageWithName:selectedImageName];
if (iOS7) {
// 声明这张图片用原图(别渲染)
selectedImage = [selectedImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
childVc.tabBarItem.selectedImage = selectedImage;
5:给每一个子控制器包装一层导航控制,主要自定义导航控制器,为了设置导航控制器上item的属性。新建类继承UINavigationController。1:因为只要是自定义了导航控制器,则系统的侧滑返回效果就会消失,只要在viewDidLoad里将手势代理清空即可:self.interactivePopGestureRecognizer.delegate = nil;
2:自定义UINavigationBar 和 UIBarButtonItem:1:其中UINavigationBar为整个导航栏,设置其主体主要设置的是导航栏的背景色,背景图片,title文字的属性。UIBarButtonItem:设置的是导航栏上左右按钮的图片或是文字属性。3:UINavigationBar *appearance = [UINavigationBar appearance];UIBarButtonItem *appearance = [UIBarButtonItem appearance];通过appearance方法返回的两个对象是全局对象,通过设置这两个对象的属性,就可以达到整个项目中全局设置,因为在整个项目中只需要设置一次,所以不需要在ViewDidload里去设置,因为没创建一次对象,ViewDidload就会调用一次,这样导航栏的主题就会被重复设置,所以将设置导航栏主题的方法封装在,+ (void)initialize,该方法只会在该类第一次使用,也就是该类的第一个方法被调用之前调用,有且只调用一次。在内部封装的方法应为类方法,用self去调用,在类方法中self指向的是当前的类,在对象方法中self指向的是当前的对象。
3:设置UINavigationBar属性:获得全局导航栏对象appearance,1:可以设置其背景色tintColor ,2:背景图片:setBackgroundImage forBarMetrics: 第二个参数为默认即可 2:设置title的文字属性: [appearance setTitleTextAttributes:textAttrs];不用设置状态。(在开发中有些方法是过期会有警告,其实也能用,只不过是有新的方法可以代替它),设置title字体的偏移量也就是阴影效果,textAttrs[NSShadowAttributeName] = [NSValue valueWithUIOffset:UIOffsetZero];其中CGRect,CGPoint,UIOffset 都是结构体,只有封装成NSValue对象,才可以放到数组或是字典中。
4:设置UIBarButtonItem的主题:1:UIBarButtonItem也就是导航栏上的左右按钮,可以利用setTitleTextAttributes
forState 属性分别设置左右按钮的常态,高亮,失效状态下的文字属性。2:当左右按钮为一张图片时,给UIBarButtonItem写一个分类,设置customView自定义按钮,直接设置按钮的背景图片为该张图片就可以。
5:重写导航控制器的push方法:1:目的是拦截push方法,因为每个栈里的控制器,左右按钮都相同,所以不需要再栈里每个控制器里创建左右按钮。拦截导航控制器的push方法,当压栈的时候,就为每个即将压栈的控制器设置了左右按钮。2:在将四个主控制器包装导航控制器的时候,initWithrootViewController,已经把该四个控制器压入栈里,此时会调用导航控制器的push方法,
/**
* 能拦截所有push进来的子控制器
*/
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (self.viewControllers.count > 0) { // 如果现在push的不是栈底控制器(最先push进来的那个控制器)
viewController.hidesBottomBarWhenPushed = YES;
// 设置导航栏按钮
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_back" highImageName:@"navigationbar_back_highlighted" target:self action:@selector(back)];
viewController.navigationItem.rightBarButtonItem = [UIBarButtonItem itemWithImageName:@"navigationbar_more" highImageName:@"navigationbar_more_highlighted" target:self action:@selector(more)];
}
[super pushViewController:viewController animated:animated];
}
1:重写的push方法,1:传入的viewController为即将压栈的控制器,重写系统的方法,不要忘记调用super。super方法在左后调用,是避免为四个主控制器也设置左右按钮 2:拿到导航控制器栈里的子控制器数组:nav.viewControllers(self.viewControllers),判断子控制器数组里的控制器个数 > 0,则表示不是栈底控制器。此时可以拿到即将压栈的控制器设置左右item:viewController.navigationItem.leftBarButtonItem。2:对左右按钮为图片的直接将图片设置为btn的背景图片,给UIBarButtonItem写一个分类,传入normalImage ,HighlightedImage,target,action,自定义btn,设置不同状态下的背景图片,一般对于图片按钮来说,按钮的大小就设置为图片的大小。对于button来说,获得当前的image,title,backgroundImage的方法,btn.currentTtitle,btn.currentImage,btn.currentBackgroundImage 3:若想调整左右按钮距离屏幕的左右间距:可以自定navbar继承系统的bar,重写layoutSubView方法,遍历子控件数组,分别找到左右按钮,调整左右按钮的frame。再利用kvc的setValue forKeyPath,来替换掉系统的navBar(只需创建自定义bar的对象,因为继承于系统,所以系统所声明的非私有的属性方法变量都会继承,不用设置frame,第一个参数为创建的自定义navbar对象,第二个参数为导航控制器或是tabbar控制器下的navbar,或是tabbar以属性定义的对象) 4:当push压栈的时候,需要隐藏tabBar,此时可在拦截的push方法中,拿到即将压栈的控制器,调用
//0:当push的时候隐藏dock栏 viewController.hidesBottomBarWhenPushed = YES;
2:自定义tabBar的封装:1:+(instancetype)tabBar;return [self alloc ]init]; 类方法快速返回一个对象,alloc内部会调用alloc initWithFrame方法,所以重写alloc initWithFrame方法,在此方法内先设置自身的属性:可以设置tabbar的背景图片self.backgroundImage 或是选中的背景图片,self.selectionIndicatorImage = [UIImage imageWithName:@"navigationbar_button_background"];2:懒加载子控件,设置控件的属性,将控件添加到父视图上。创建中间的加号按钮,常态,高亮状态下的背景图片,setImage设置常态高亮状态下的图片,添加监听,添加到父视图上。3:在layoutSubView里设置控件的frame,(若为图片按钮,有不同状态,就设图片为整个按钮背景,分贝设常态,高亮,选中状态下的背景图片,按钮的大小为图片的大小:btn.currentTitle,btn.currentImage,btn.currentBackgroundImage即可获得当前显示的图片和标题。)如何拿到控件设置frame,1:属性,成员变量,枚举tag值 2:大数组思想:装在大数组,当一个控件上分别有不同的控件的时候,可以两两分别装在数组里,用到的时候再从数组里取出 3:继承UIView的可以遍历self.subViews,遍历子控件的数组,做条件过滤,找到停止遍历。若是UIScrollView,会多出两个控件横竖滚动条,直接隐藏就不会显示了,隐藏后可直接遍历子控件数组,也可以提前给View设置tag值,当遍历时,找到父view后,通过tag值将view取出 。当父view上有多个控件时,还可以遍历子控件数组,做过滤,当找到需要设置frame的控件后,需要根据index设置frame,此时在循环中无法拿到index,就自己定义一个index,找到控件后执行index++,根据控件的索引来设置frame 4:设置按钮的tag值:1:直接设置(可为0)2:枚举tag值,用于按钮的回调,根据枚举值判断是哪个按钮 3:btn.tag =self.subViews.count,适用于将按钮取出,或是应用于本类中 。4:所以设置加号按钮的size为图片的size,通过btn.currentBackgroundImage把图片取出(一般给系统控件赋值时,调用的是系统控件的set方法,若想在其他地方获得该值,可以调用其系统控件的get方法,例如btn的三个方法,lable.font,vc.title等),如果控件需要居中显示的话,就设置控件的center属性,例如新特性界面的pageControl的frame设置方法。5:虽然是自定义的tabBar,但是继承的是系统的tabBar,已经添加了四个子控制器,所以自定义的tabbar上已经添加了四个按钮。需要重新调整四个按钮的距离。在layoutSubView里重新调整四个按钮的frame。遍历子控件数组,做条件过滤,并打印,有的是系统没有声明的属性,打印出只是一个字符串,利用NSSClassFromString();方法将字符串转化为类来判断是否属于.
/**
* 设置所有tabbarButton的frame
*/
- (void)setupAllTabBarButtonsFrame
{
int index = 0;
// 遍历所有的button
for (UIView *tabBarButton in self.subviews) {
// 如果不是UITabBarButton, 直接跳过
if (![tabBarButton isKindOfClass:NSClassFromString(@"UITabBarButton")]) continue;
// 根据索引调整位置
[self setupTabBarButtonFrame:tabBarButton atIndex:index];
// 索引增加
index++;
}
}
/**
* 设置某个按钮的frame
*
* @param tabBarButton 需要设置的按钮
* @param index 按钮所在的索引
*/
- (void)setupTabBarButtonFrame:(UIView *)tabBarButton atIndex:(int)index
{
// 计算button的尺寸
CGFloat buttonW = self.width / (self.items.count + 1);
CGFloat buttonH = self.height;
tabBarButton.width = buttonW;
tabBarButton.height = buttonH;
if (index >= 2) {
tabBarButton.x = buttonW * (index + 1);
} else {
tabBarButton.x = buttonW * index;
}
tabBarButton.y = 0;
}
@end
设置frame的思路:1:tabBar上已经添加上了5个按钮,就让这5个按钮评分tabBar的宽度,注意:self.ietms.count,虽然此时tabBar已经有5个按钮了,但是个数为4,它指的是加入tabBarcontroller中的子控制器的个数。高度为自身tabBar的高度,y值为0,x值的设置,前两个可以根据索引正常设置buttonW * index,到后两个的时候:buttonW * (index + 1)。2:当在layoutSubView里设置frame时,遍历取出控件,根据index设置frame,如果无法遍历,可以自己构造index,实现index++,将index传过去。
5:tabBar点击中间加号按钮的回调:1:回调可用协议代理,block ,通知,前两个用于层级较浅的回调,通知适用于层级较深的回调 2:其中两个控制器需要回调数据时,若两个控制器需要实时相互回调数据,可用block和协议代理,设置返回值,当一端回调数据后,处理完数据后,还可以返回一些给原控制器提供回调数据。3:协议代理的方法默认为@require,注意协议代书写的规范性,仿照tableView的代理来设置
6:在tabBarController里利用kvc替换系统的tabBar:不用设置frame
/**
* 创建自定义tabbar
*/
- (void)addCustomTabBar
{
// 创建自定义tabbar
HMTabBar *customTabBar = [[HMTabBar alloc] init];
customTabBar.tabBarDelegate = self;
// 更换系统自带的tabbar
[self setValue:customTabBar forKeyPath:@"tabBar"];
}
7:UIImage的封装:分类封装
#import <UIKit/UIKit.h>
@interface UIImage (Extension)
/**
* 根据图片名自动加载适配iOS6\7的图片
*/
+ (UIImage *)imageWithName:(NSString *)name;
/**
* 根据图片名返回一张能够自由拉伸的图片
*/
+ (UIImage *)resizedImage:(NSString *)name;
@end
#import "UIImage+Extension.h"
@implementation UIImage (Extension)
+ (UIImage *)imageWithName:(NSString *)name
{
UIImage *image = nil;
if (iOS7) { // 处理iOS7的情况
NSString *newName = [name stringByAppendingString:@"_os7"];
image = [UIImage imageNamed:newName];
}
if (image == nil) {
image = [UIImage imageNamed:name];
}
return image;
}
+ (UIImage *)resizedImage:(NSString *)name
{
UIImage *image = [UIImage imageWithName:name];
return [image stretchableImageWithLeftCapWidth:image.size.width * 0.5 topCapHeight:image.size.height * 0.5];
}
@end
赋值代码的三部曲:1:外部先定义一个变量 2:中间根据判断条件拿到变量赋值 3:重新给变量赋值(若判断条件不符合,则变量的值没有改变,若是符合,则发生了改变)