一:主框架分析:
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:重新给变量赋值(若判断条件不符合,则变量的值没有改变,若是符合,则发生了改变)