最终效果图:
主控制器
// // BeyondViewController.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 主控制器包括两个部分:左边的Dock,右边的contentView,其中左边Dock又包括三个部分(顶部的头像按钮、中部的选项卡、底部的一个整体),其中底部的整体 包含三个按钮「说说、拍照、日记」,中部的选项卡包含六个自定义item「全部动态、与我相关、照片墙、电子相框、好友、更多」 #import "BeyondViewController.h" // 控制器 #import "BeyondNavigationController.h" #import "NanaController.h" // Dock #import "Dock.h" #import "DockMiddleTabBar.h" #import "DockBottomView.h" #import "DockTopIconBtn.h" // 协议 #import "DockMiddleTabBarDelegate.h" #import "DockBottomViewDelegate.h" @interface BeyondViewController () <DockMiddleTabBarDelegate, DockBottomViewDelegate> @property (nonatomic, weak) Dock *dock; // 存放右边子控制器的view @property (nonatomic, weak) UIView *contentView; @end @implementation BeyondViewController #pragma mark - 生命周期方法 - (void)viewDidLoad { [super viewDidLoad]; // 0.设置控制器背景色 为全局深灰色 self.view.backgroundColor = kGlobalBgColor; // 1.初始化Dock [self setupDock]; // 2.初始化contentView [self setupContentView]; // 3.初始化所有子控制器 [self setupChildViewControllers]; // 4.默认点击头像 [self iconBtnClicked]; } #pragma mark - 左边Dock // 1.初始化Dock - (void)setupDock { // 1.添加dock Dock *dock = [Dock allocInit]; // 重要~~~高度自动伸缩 dock.autoresizingMask = UIViewAutoresizingFlexibleHeight; dock.height = self.view.height; [self.view addSubview:dock]; self.dock = dock; // 2.根据当前的屏幕方向设置dock的属性 [self.dock rotate:UIInterfaceOrientationIsLandscape(self.interfaceOrientation)]; // 3.设置tabbar的代理 self.dock.tabBar.delegate = self; // 4.监听头像的点击 [self.dock.iconBtn addTarget:self action:@selector(iconBtnClicked) forControlEvents:UIControlEventTouchUpInside]; // 5.设置bottomView的代理,监听bottomView内部的三个按钮点击 self.dock.bottomView.delegate = self; } // 4.点击头像 - (void)iconBtnClicked { // 子控制器的个数 共 7个 0~5对应TabBar,索引为6才是头像对应的控制器 int count = self.childViewControllers.count; // 遍历,获得当前正在显示的控制器的索引 int from = 0; for (int i = 0; i<count; i++) { UIViewController *vc = self.childViewControllers[i]; if (vc.view.superview) { // 如果有控制器正在显示 from = i; break; } } // 最后一个子控制器的索引,即头像对应的控制器 int iconVCIndex = count - 1; // 调用自定义方法(实为代理方法),从当前正在显示的控制器切换到头像控制器 [self tabBar:nil didSelectButtonFrom:from to:iconVCIndex]; // 重要~~~让tabbar里面的当前选中的按钮 取消选中 [self.dock.tabBar deSelectCurrentItem]; } #pragma mark - 右边ContentView // 2.初始化contentView - (void)setupContentView { // 1.创建ContentView UIView *contentView = [UIView allocInit]; // dock宽 CGFloat dockW = self.dock.width; // x contentView.x = dockW; // 屏幕宽 CGFloat screenW = UIInterfaceOrientationIsLandscape(self.interfaceOrientation) ?1024:768; // 宽 contentView.width = screenW - dockW; // 高 contentView.height = self.view.height; contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; [self.view addSubview:contentView]; self.contentView = contentView; // 2.UIView的分类方法 添加 pan 手势 (为弹簧效果作准备) [self.contentView addPanGestureWithTarget:self action:@selector(dragContentView:)]; } // 弹簧效果 触发了pan手势就会调用(开始-->ing-->end) - (void)dragContentView:(UIPanGestureRecognizer *)pan { // 分类方法,弹簧效果,仅是可拖动的弹簧效果,没有进行边界检测 [pan addFlexableEffect]; } #pragma mark - 所有子控制器 // 3.初始化所有子控制器 - (void)setupChildViewControllers { // tabbar对应的6个子控制器 // 0~5 对应TabBar上6个item UIViewController *vc0 = [UIViewController allocInit]; vc0.title = @"全部动态"; [self setupOneChildViewController:vc0]; UIViewController *vc1 = [UIViewController allocInit]; vc1.title = @"与我相关"; [self setupOneChildViewController:vc1]; UIViewController *vc2 = [UIViewController allocInit]; vc2.title = @"照片墙"; [self setupOneChildViewController:vc2]; UIViewController *vc3 = [UIViewController allocInit]; vc3.title = @"电子相框"; [self setupOneChildViewController:vc3]; UIViewController *vc4 = [UIViewController allocInit]; vc4.title = @"好友"; [self setupOneChildViewController:vc4]; UIViewController *vc5 = [UIViewController allocInit]; vc5.title = @"更多"; [self setupOneChildViewController:vc5]; // 7.点击主控制器左边Dock 最上方的头像,来到nana控制器 NanaController *nana =[NanaController allocInit]; [self setupOneChildViewController:nana]; } // 3.1.自定义方法,用导航控制器,包装一个子控制器 (也可以重写父类的addChildViewController方法) - (void)setupOneChildViewController:(UIViewController *)vc { // 不要自动扩张至全屏 vc.edgesForExtendedLayout = UIRectEdgeNone; BeyondNavigationController *nav = [[BeyondNavigationController alloc] initWithRootViewController:vc]; [self addChildViewController:nav]; } #pragma mark - 父类方法-屏幕即将旋转 // 当屏幕即将旋转的时候调用 // toInterfaceOrientation 旋转完毕后的最终方向 // duration 旋转动画所花费的时间 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { // 1.设置Dock //控制器监测到了 屏幕旋转的事件,通知内部子控件Dock [self.dock rotate:UIInterfaceOrientationIsLandscape(toInterfaceOrientation)]; // 2.设置contentView // dock宽 CGFloat dockW = self.dock.width; // x self.contentView.x = dockW; // 屏幕宽 (因为是willRotate,所以要反着来) CGFloat screenW = UIInterfaceOrientationIsLandscape(self.interfaceOrientation) ?768:1024; // 宽 self.contentView.width = screenW - dockW; } #pragma mark - TabBar的代理方法 - (void)tabBar:(DockMiddleTabBar *)tabBar didSelectButtonFrom:(int)from to:(int)to { // 1.新控制器(实为导航控制器) BeyondNavigationController *newNaviVC = self.childViewControllers[to]; // 防止重复点击 if (newNaviVC.view.superview) return; // 2.设置新控制器的frame填充整个contentView newNaviVC.view.frame = self.contentView.bounds; // 3.取出导航控制器的根控制器设置背景色 UIViewController *newRootVC = [newNaviVC.childViewControllers firstObject]; newRootVC.view.backgroundColor = kColor(212, 212, 212); // 4.旧控制器 BeyondNavigationController *oldNaviVC; // 取出最后一个子控制器 (即头像按钮控制器) BeyondNavigationController *lastNaviVC = [self.childViewControllers lastObject]; if (lastNaviVC.view.superview) { // 特例,如果是最后一个添加的头像控制器在显示 (头像按钮控制器正在显示,则旧控制器就是头像控制器) oldNaviVC = lastNaviVC; } else { // 如果是 0 ~ 5这几个TabBar里面的控制器,则可以直接 统一切换 oldNaviVC = self.childViewControllers[from]; } // 5.转场动画 调用UIView的分类方法,实现三维转场动画 [self.contentView switchOldView:oldNaviVC.view toNewView:newNaviVC.view]; } #pragma mark - bottomView代理方法 - (void)bottomMenu:(DockBottomView *)bottomView didClickButton:(BottomViewBtnType)btnType { switch (btnType) { case BottomViewBtnTypeBlog: // 日志 case BottomViewBtnTypeMood: // 心情 case BottomViewBtnTypePhoto: // 照片 { NanaController *nanaVC = [NanaController allocInit]; BeyondNavigationController *nav = [[BeyondNavigationController alloc] initWithRootViewController:nanaVC]; // 设置modal 的类型: nav.modalPresentationStyle = UIModalPresentationFormSheet; [self presentViewController:nav animated:YES completion:nil]; break; } default: break; } } @end
View的封装
Dock
// // Dock.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 主控制器左边的Dock,其包括三个部分(顶部的头像按钮、中部的选项卡、底部的一个整体),其中底部的整体 包含三个按钮「说说、拍照、日记」,中部的选项卡包含六个自定义item「全部动态、与我相关、照片墙、电子相框、好友、更多」 #import <UIKit/UIKit.h> @class DockTopIconBtn, DockMiddleTabBar,DockBottomView; @interface Dock : UIView // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape; #pragma mark - 全部提供给控制器访问,设置代理等 @property (weak, nonatomic, readonly) DockTopIconBtn *iconBtn; @property (weak, nonatomic, readonly) DockMiddleTabBar *tabBar; @property (weak, nonatomic, readonly) DockBottomView *bottomView; @end
// // Dock.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 主控制器左边的Dock,其包括三个部分(顶部的头像按钮、中部的选项卡、底部的一个整体),其中底部的整体 包含三个按钮「说说、拍照、日记」,中部的选项卡包含六个自定义item「全部动态、与我相关、照片墙、电子相框、好友、更多」 #import "Dock.h" #import "DockTopIconBtn.h" #import "DockMiddleTabBar.h" #import "DockBottomView.h" @implementation Dock #pragma mark - 生命周期方法 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 1.添加子控件 头像 [self setupiconBtn]; // 2.添加子控件 选项卡 [self setupTabBar]; // 3.添加子控件 底部整体 [self setupBottomView]; } return self; } // 1.添加子控件 头像 - (void)setupiconBtn { DockTopIconBtn *iconBtn = [[DockTopIconBtn alloc] init]; [self addSubview:iconBtn]; _iconBtn = iconBtn; } // 2.添加子控件 选项卡 - (void)setupTabBar { DockMiddleTabBar *tabBar = [[DockMiddleTabBar alloc] init]; // 粘着底部 tabBar.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; [self addSubview:tabBar]; _tabBar = tabBar; } // 3.添加子控件 底部整体 - (void)setupBottomView { DockBottomView *bottomView = [[DockBottomView alloc] init]; // 粘着底部 bottomView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin; [self addSubview:bottomView]; _bottomView = bottomView; } #pragma mark - 拦截外部屏幕变化时的传递过来的消息 // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape { // 1.转达消息,首先 调整底部菜单,才能设置其他的子控件 [self.bottomView rotate:lanscape]; // 2.然后才 设置dock自己的宽度 跟随 底部的宽度 self.width = self.bottomView.width; // 3.转达消息,然后才调整 tabbar [self.tabBar rotate:lanscape]; self.tabBar.y = self.bottomView.y - self.tabBar.height; // 4.转达消息,最后才调整 头像 [self.iconBtn rotate:lanscape]; } @end
Dock的底部三个按钮作为一个整体
// // DockBottomView.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 左侧dock 的底部的一个整体,包含 三个按钮,分别是 说说、拍照、日记 #import <UIKit/UIKit.h> @protocol DockBottomViewDelegate; @interface DockBottomView : UIView // 定义枚举,分别代表底部整体里面的三个按钮,它们是 说说、拍照、日记 typedef enum { BottomViewBtnTypeMood, // 心情 BottomViewBtnTypePhoto, // 照片 BottomViewBtnTypeBlog // 日志 } BottomViewBtnType; // 代理,告诉代理点击的按钮类型 @property (nonatomic, weak) id<DockBottomViewDelegate> delegate; // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape; @end
// // DockBottomView.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "DockBottomView.h" // 通知代理,点击的按钮类型 #import "DockBottomViewDelegate.h" @implementation DockBottomView #pragma mark - 生命周期 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 初始化3个按钮,并且绑定tag为定义好的枚举类型(通知代理时用) [self setupButtonWithIcon:@"tabbar_mood" tag:BottomViewBtnTypeMood]; [self setupButtonWithIcon:@"tabbar_photo" tag:BottomViewBtnTypePhoto]; [self setupButtonWithIcon:@"tabbar_blog" tag:BottomViewBtnTypeBlog]; } return self; } // 自定义方法,创建一个按钮,参数:图片名,绑定的tag为按钮枚举类型(通知代理时用) - (void)setupButtonWithIcon:(NSString *)icon tag:(BottomViewBtnType)tag { UIButton *button = [[UIButton alloc] init]; // 绑定的tag为按钮枚举类型(通知代理时用) button.tag = tag; // 图标按原始大小,文字无 // 选中时的背景图片 拉伸 // 监听点击事件,好通知代理,并且传递按钮的tag过去 [button setBtnIcon:icon title:nil selectedBgImg:@"tabbar_separate_selected_bg" target:self action:@selector(buttonClick:) event:UIControlEventTouchUpInside]; [self addSubview:button]; } #pragma mark - 监听点击 - (void)buttonClick:(UIButton *)button { if ([self.delegate respondsToSelector:@selector(bottomMenu:didClickButton:)]) { [self.delegate bottomMenu:self didClickButton:button.tag]; } } #pragma mark - 拦截外部屏幕变化时的传递过来的消息 // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape { // 三个按钮 int count = self.subviews.count; if (lanscape) { // 横屏时,横着排,宽度大一些 self.width = kBottomBtnLanscapeW * count; self.height = kBottomBtnH; // 一一设置frame,横着排 for (int i = 0; i<count; i++) { UIButton *button = self.subviews[i]; button.x = i * kBottomBtnLanscapeW; button.y = 0; button.width = kBottomBtnLanscapeW; button.height = kBottomBtnH; } } else { // 竖屏,立着排,宽度窄一些 self.width = kBottomBtnPortraitW; self.height = kBottomBtnH * count; // 一一设置frame,立着排 for (int i = 0; i<count; i++) { UIButton *button = self.subviews[i]; button.x = 0; button.y = i * kBottomBtnH; button.width = kBottomBtnPortraitW; button.height = kBottomBtnH; } } // 底部整体 自己的y值:Dock的高度 - 自己的高度 self.y = self.superview.height - self.height; } @end
Dock底部整体 代理协议
// // DockBottomViewDelegate.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 左侧dock 的底部的一个整体,包含 三个按钮,分别是 说说、拍照、日记 // 定义协议,目的是通知外界:底部的一个整体中某一个按钮被点击了 #import <Foundation/Foundation.h> @class DockBottomView; @protocol DockBottomViewDelegate <NSObject> @optional - (void)bottomMenu:(DockBottomView *)bottomView didClickButton:(BottomViewBtnType)btnType; @end
Dock中部的TabBar
// // DockMiddleTabBar.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 左侧dock 的中部的 TabBar,选项卡,共有 6个, // 它们分别是:「全部动态」「与我相关」「照片墙」「电子相框」「好友」「更多」 #import <UIKit/UIKit.h> @protocol DockMiddleTabBarDelegate; @interface DockMiddleTabBar : UIView // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape; @property (nonatomic, weak) id<DockMiddleTabBarDelegate> delegate; // 供控制器调用,取消选中 当前选中的Btn - (void)deSelectCurrentItem; @end
// // DockMiddleTabBar.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "DockMiddleTabBar.h" // 监听点击,通知代理 #import "DockMiddleTabBarDelegate.h" #import "DockMiddleTabBarItem.h" // BeyondViewController成为DockMiddleTabBar的代理 // BeyondViewController监听DockMiddleTabBar内部按钮的点击事件 @interface DockMiddleTabBar() // 选中的那个item @property (weak, nonatomic) DockMiddleTabBarItem *selectedButton; @end @implementation DockMiddleTabBar #pragma mark - 生命周期 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 一次性,初始化6个按钮 [self setupButtonWithIcon:@"tab_bar_feed_icon" title:@"全部动态"]; [self setupButtonWithIcon:@"tab_bar_passive_feed_icon" title:@"与我相关"]; [self setupButtonWithIcon:@"tab_bar_pic_wall_icon" title:@"照片墙"]; [self setupButtonWithIcon:@"tab_bar_e_album_icon" title:@"电子相框"]; [self setupButtonWithIcon:@"tab_bar_friend_icon" title:@"好友"]; [self setupButtonWithIcon:@"tab_bar_e_more_icon" title:@"更多"]; } return self; } // 自定义方法,添加一个item - (void)setupButtonWithIcon:(NSString *)icon title:(NSString *)title { DockMiddleTabBarItem *button = [[DockMiddleTabBarItem alloc] init]; // 绑定tag 0~5 button.tag = self.subviews.count; // 设置标题 // 监听点击 // 图标按原始大小 // 选中时的背景图片 拉伸 [button setBtnIcon:icon title:title selectedBgImg:@"tabbar_separate_selected_bg" target:self action:@selector(buttonClick:) event:UIControlEventTouchDown]; [self addSubview:button]; } #pragma mark - 监听点击,通知代理 - (void)buttonClick:(DockMiddleTabBarItem *)button { // 1.通知代理 if ([self.delegate respondsToSelector:@selector(tabBar:didSelectButtonFrom:to:)]) { [self.delegate tabBar:self didSelectButtonFrom:self.selectedButton.tag to:button.tag]; } // 2.标准三步曲,切换按钮状态 [button switchSelectedStatusWithOldBtn:self.selectedButton]; } #pragma mark - 拦截外部屏幕变化时的传递过来的消息 // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape { // 根据子控件个数,设置不同屏幕下的frame int count = self.subviews.count; // 选项卡的 宽 始终为dock的宽 self.width = self.superview.width; // 选项卡的 高 和 子item的个数有关 self.height = kBottomBtnH * count; // 遍历子控件,一一设置frame,立着排列 for (int i = 0; i<count; i++) { DockMiddleTabBarItem *button = self.subviews[i]; button.width = self.width; button.height = kBottomBtnH; button.x = 0; button.y = i * button.height; } } #pragma mark - 仅供外界(控制器)调用 - (void)deSelectCurrentItem { self.selectedButton.selected = NO; } @end
Dock中部TabBar的代理要实现的协议
// // DockMiddleTabBarDelegate.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // DockMiddleTabBar定义好的协议,到时候监听到点击时,通知代理 #import <Foundation/Foundation.h> @class DockMiddleTabBar; @protocol DockMiddleTabBarDelegate <NSObject> @optional - (void)tabBar:(DockMiddleTabBar *)tabBar didSelectButtonFrom:(int)from to:(int)to; @end
Dock中部的选项卡里面的一个按钮
// // DockMiddleTabBarItem.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // Dock中部的选项卡里面的一个按钮 #import "DockMiddleTabBarItem.h" // 横屏时,图片所占的宽度比 #define kImageLanscpaeWidthRatio 0.4 @implementation DockMiddleTabBarItem #pragma mark - 生命周期 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 图片的伸缩 模式 self.imageView.contentMode = UIViewContentModeCenter; // 标题的字体 self.titleLabel.font = [UIFont systemFontOfSize:20]; } return self; } #pragma mark - 父类方法 // 不需要高亮状态,只要选中即可 - (void)setHighlighted:(BOOL)highlighted {} // 调整 图片的frame - (CGRect)imageRectForContentRect:(CGRect)contentRect { if (self.width > kBottomBtnLanscapeW) { // 横屏时,左图,右文字 CGFloat imageW = self.width * kImageLanscpaeWidthRatio; CGFloat imageH = self.height; return CGRectMake(0, 0, imageW, imageH); } else { // 竖屏时,只有图片,没有文字 return self.bounds; } } // 调整 标题的frame - (CGRect)titleRectForContentRect:(CGRect)contentRect { if (self.width > kBottomBtnLanscapeW) { // 横屏时,左图,右文字 CGFloat titleX = self.width * kImageLanscpaeWidthRatio; CGFloat titleW = self.width - titleX; CGFloat titleH = self.height; return CGRectMake(titleX, 0, titleW, titleH); } else { // 竖屏时,只有图片,没有文字 return CGRectZero; } } @end
Dock顶部的头像按钮
// // DockTopIconBtn.h // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 左侧dock 的顶部的 头像按钮 #import <UIKit/UIKit.h> @interface DockTopIconBtn : UIButton // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape; @end
// // DockTopIconBtn.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 左侧dock 的顶部的 头像按钮 #import "DockTopIconBtn.h" @implementation DockTopIconBtn #pragma mark - 生命周期方法 - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // 设置头像按钮 的图片 [self setImage:[UIImage imageStretchedWithName:@"nanaLogo.jpg"] forState:UIControlStateNormal]; // 设置头像按钮 的标题 [self setTitle:@"nana" forState:UIControlStateNormal]; // 设置头像按钮 的标题对齐方式 self.titleLabel.textAlignment = NSTextAlignmentCenter; // 给图层加圆角 self.imageView.layer.cornerRadius = 10; } return self; } #pragma mark - 父类方法 // 调整 图片的frame - (CGRect)imageRectForContentRect:(CGRect)contentRect { if (self.width > kBottomBtnPortraitW) { // 横屏 // 图片宽为按钮宽,正方形 CGFloat imageW = self.width; CGFloat imageH = imageW; return CGRectMake(0, 0, imageW, imageH); } else { // 竖屏时,图片占据整个按钮 return self.bounds; } } // 调整 标题的frame - (CGRect)titleRectForContentRect:(CGRect)contentRect { if (self.width > kBottomBtnPortraitW) { // 横屏 // 标题在图片下方,占据按钮除图片以外的空间 CGFloat titleY = self.width; CGFloat titleW = self.width; CGFloat titleH = self.height - titleY; return CGRectMake(0, titleY, titleW, titleH); } else { // 竖屏时,标题隐藏 return CGRectZero; } } #pragma mark - 拦截外部屏幕变化时的传递过来的消息 // 当外界收到屏幕旋转的消息时,会调用该方法 告之我,我会拦截此方法,调整好自己在相应的屏幕下的frame,接着我再把此消息传达给我自己内部的子控件们,让其调整frame - (void)rotate:(BOOL)lanscape { if (lanscape) { // 横屏 // 头像按钮的宽为 父控件Dock的宽度的三分之一 self.width = self.superview.width * 0.35; // 头像按钮的高为 宽+40, 其中40是为标题预留的 self.height = self.width + 40; // 头像按钮居dock的顶部中心 self.y = 60; self.x = (self.superview.width - self.width) * 0.5; } else { // 竖屏时,仍占dock的顶部中心,但此时宽高一致,标题不再显示 self.y = 40; self.x = 10; self.width = self.superview.width - 2 * self.x; self.height = self.width; } } @end
自定义Segement控件及其使用
// // BeyondSegment.h // 28_QQ空间 // // Created by beyond on 14-9-2. // Copyright (c) 2014年 com.beyond. All rights reserved. // 自定义控件 segmentControl,每一个按钮都是segmentBarBtn #import <UIKit/UIKit.h> @interface SegmentBar : UIView // 字串 数组,根据数组长度 决定 生成多少个SegmentBarBtn,并为其title赋值 @property (nonatomic, strong) NSArray *titlesArr; // 设置当前选中的segmentBarBtn @property (nonatomic, assign) int selectedSegmentIndex; @end
// // BeyondSegment.m // 28_QQ空间 // // Created by beyond on 14-9-2. // Copyright (c) 2014年 com.beyond. All rights reserved. // 自定义控件 segmentControl,每一个按钮都是segmentBarBtn #import "SegmentBar.h" #import "SegmentBarBtn.h" @interface SegmentBar() @property (nonatomic, weak) SegmentBarBtn *selectedSegment; @end @implementation SegmentBar #pragma 拦截setter,添加内部子控件 - (void)setTitlesArr:(NSArray *)titlesArr { _titlesArr = titlesArr; // 1.为健壮性,先移除以前创建的所有按钮 [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; // 2.添加新的按钮 int count = titlesArr.count; for (int i = 0; i<count; i++) { SegmentBarBtn *segment = [[SegmentBarBtn alloc] init]; // 设置tag segment.tag = i; // 设置文字 [segment setBtnTitleColorForNormal:titlesArr[i]]; // 设置背景 NSString *bgName = nil; NSString *selectedBgName = nil; if (i == 0) { // 最左边 bgName = @"SegmentedControl_Left_Normal"; selectedBgName = @"SegmentedControl_Left_Selected"; } else if (i == count - 1) { // 最右边 bgName = @"SegmentedControl_Right_Normal"; selectedBgName = @"SegmentedControl_Right_Selected"; } else { // 中间 bgName = @"SegmentedControl_Normal"; selectedBgName = @"SegmentedControl_Selected"; } // 设置正常和选中状态下的背景图片(已拉伸) [segment setBtnBgImgForNormalAndSelectedWithName:bgName selectedName:selectedBgName]; // 监听点击 [segment addTarget:self action:@selector(segmentClick:) forControlEvents:UIControlEventTouchDown]; [self addSubview:segment]; } } // 监听点击 - (void)segmentClick:(SegmentBarBtn *)segment { // 标准三步曲,切换选中按钮状态 [segment switchSelectedStatusWithOldBtn:self.selectedSegment]; } // For循环遍历数据时,每添加一个按钮时,就重新调整一下,它们的宽,x - (void)layoutSubviews { [super layoutSubviews]; int count = self.subviews.count; // 平铺,宽度均分 CGFloat buttonW = self.width / count; CGFloat buttonH = self.height; // 取出每一个,设置x for (int i = 0; i<count; i++) { SegmentBarBtn *button = self.subviews[i]; button.width = buttonW; button.height = buttonH; button.y = 0; button.x = i * buttonW; } } #pragma mark - 供外界调 // 开放接口,供外界调用 setter - (void)setSelectedSegmentIndex:(int)selectedSegmentIndex { int count = self.titlesArr.count; // 健壮性检测 if (selectedSegmentIndex < 0 || selectedSegmentIndex >= count) return; // 取出对应位置的按钮 SegmentBarBtn *segment = self.subviews[selectedSegmentIndex]; // 调用自定义方法,选中该索引下的按钮 [self segmentClick:segment]; } // 开放接口,供外界调用 getter - (int)selectedSegmentIndex { return self.selectedSegment.tag; } @end
在Nana控制器中使用自定义控件segment,
及用代码创建storyboard中描述的控制器
// // NanaController.m // 28_QQ空间 // // Created by beyond on 14-9-1. // Copyright (c) 2014年 com.beyond. All rights reserved. // 点击主控制器左边Dock 最上方的头像,来到此控制器 #import "NanaController.h" #import "SegmentBar.h" @interface NanaController () //@property (nonatomic, weak) SegmentBar *sc; @end @implementation NanaController // 1.设置导航栏左边按钮 - (void)setupLeftBarBtn { self.title = @"I'm nana"; // 分类方法 self.navigationItem.leftBarButtonItem = [UIBarButtonItem barButtonItemwithTitle:@"退出系统" style:UIBarButtonItemStyleDone target:self action:@selector(logout)]; } #pragma mark - 生命周期 - (void)viewDidLoad { [super viewDidLoad]; // 1.设置导航栏左边按钮 [self setupLeftBarBtn]; // 2.设置导航栏的titleView为自定义的segment控件 [self setupSegmentBar]; } // 2.设置导航栏的titleView为自定义的segment控件 - (void)setupSegmentBar { // 数据源模型决定 view NSArray *titlesArr = @[@"全部", @"好友动态", @"我的关注"]; if (iOS7) { // iOS7使用自定义的扁平化风格 UISegmentedControl *sc = [[UISegmentedControl alloc] initWithItems:titlesArr]; sc.frame = CGRectMake(0, 0, 300, 32); sc.selectedSegmentIndex = 0; // 使用tintColor进行着色 sc.tintColor = [UIColor grayColor]; self.navigationItem.titleView = sc; } else { SegmentBar *sc = [[SegmentBar alloc] init]; sc.titlesArr = titlesArr; sc.frame = CGRectMake(0, 0, 300, 32); sc.selectedSegmentIndex = 0; self.navigationItem.titleView = sc; } } // 点击退出登录,回到登录控制器,即创建Main.storyboard里面的初始控制器 - (void)logout { // 调用控制器的分类方法从storyboard中创建指定ID的控制器 self.view.window.rootViewController = [UIViewController allocInitFromStoryboard:nil withID:@"LoginController" bundle:nil]; } @end
时间: 2024-11-05 18:38:19