iOS_仿QQ空间_控制器的切换_自定义segmentCtrol

最终效果图:

主控制器

//
//  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

iOS_仿QQ空间_控制器的切换_自定义segmentCtrol的相关文章

仿QQ空间用一个tableview显示多种自定义cell

第一部分 要实现效果 先来看看真实QQ空间的效果吧: 从这张截图中,可以看到两种自定义的cell,其实在QQ空间的我的空间中有三种自定义的cell,那么这就是我们要实现的效果. 第二部分 实现的思路 第一步(由于没有数据源,我们只好操作plist文件): 创建我们所需要的plist文件,如下图: 这是表格样式三所需的plist文件 这是我们表格样式一所需的plist文件 这是表格样式二所需要的文件 第二步 有了素材文件之后,就好办了,我们需要把它解析出来,由于这些plist文件都是以数组形式包装

Fragment,仿QQ空间

转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9023451          在今天的这篇文章当中,我依然会以实战加理论结合的方式教大家如何设计出自己觉得很炫的UI界面.好的,话不多说,进入正题.今天的这篇文章主要是以仿QQ空间的底部菜单栏效果为主,实现的效果有: <1>实现了点击按钮时的切换图片效果: <2>实现了点击按钮时的切换界面效果: <3>实现了点击中间圆形按钮时弹出菜单以及按钮图片切

【Android UI设计与开发】第09期:底部菜单栏(四)Fragment+PopupWindow仿QQ空间最新版底部菜单栏

转载请注明出处:http://blog.csdn.net/yangyu20121224/article/details/9023451          在今天的这篇文章当中,我依然会以实战加理论结合的方式教大家如何设计出自己觉得很炫的UI界面.好的,话不多说,进入正题.今天的这篇文章主要是以仿QQ空间的底部菜单栏效果为主,实现的效果有: <1>实现了点击按钮时的切换图片效果: <2>实现了点击按钮时的切换界面效果: <3>实现了点击中间圆形按钮时弹出菜单以及按钮图片切

Html - 仿QQ空间右下角工具浮动块

仿QQ空间右下角工具浮动块 <style type="text/css"> .cy-tp-area>.cy-tp-fixbtn>.cy-tp-text { display: none; margin-top: 15px; text-align: center; text-decoration: none; } .cy-tp-area>.cy-tp-fixbtn { background-color: #fafafa; color: #8c8c8c; } .

安卓开发之Kotlin和java双实现仿qq空间下拉图片拉伸

先不扯淡看今天要实现的效果: 话说使用Kotlin实现安卓功能,那我们还是要做一点准备工作,so,你得加一点插件到eclipse或android studio.然并卵,你现在还在使用eclipse开发的话我只能提供地址Kotlin Plugin for Eclipse,然后我使用的还是死丢丢. 死丢丢(android studio)集成kotlin安卓开发 要使用android studio开发kotlin的安卓app,那么你必须有开发kotlin的环境: Kotlin插件.打开Android

IOS仿QQ空间时间显示

最近项目有类似QQ空间展示动态的UI,模仿了QQ空间的时间显示,在此记录,以备查阅. 这是QQ空间的ui: 时间显示为: 1.今天-->今天 xx:xx(今天 15:39)   2.昨天-->昨天 xx:xx(昨天 06:00)   3.前天-->前天 xx:xx(前天 19:00)   4.同一年, 例如:同一年的一月三号-->01-03 xx:xx(01-03 12:29)   5.不在同一年 --> xxxx-xx-xx(2014-12-12) 程序运行效果如下,以下为

仿QQ空间图片放缩查看

仿QQ空间图片放缩查看 仿QQ空间图片放缩查看,点击图片从原位置放大到全屏,后退从全屏缩小到原位置,效果非常好. 下载地址:http://www.devstore.cn/code/info/830.html  运行截图:   

ScrollView的阻尼回弹效果实现(仿qq空间)

玩过新浪微博,qq空间等手机客户端的童鞋,都应该清楚,在主界面向下滑动时,会有一个阻尼回弹效果,看起来挺不错,接下来我们就来实现一下这种效果,下拉后回弹刷新界面,先看效果图: 这个是编辑器里面的界面效果,不言自明: 运行效果: 正常界面下: 下拉: 下拉结束: 实现代码: 主要部分就是重写的ScrollView: package com.byl.scollower; import android.content.Context; import android.graphics.Rect; imp

iOS_28仿QQ空间登录与退出

最终效果图如下: 注意事项: 输入框的return Key Main.storyboard中为 LoginController 设置一个storyboardID, 以便可以在代码中通过Storyboard对象实例,创建Main.storyboard里面的控制器 仿QQ窗口抖动 dispach_after模拟延时 输入框的return Key的不同处理方式 Login控制器代码 // // LoginController.m // 28_QQ空间 // // Created by beyond o