需求 - 5 - 悬浮框 - 1

来源:互联网 发布:信息技术软件介绍ppt 编辑:程序博客网 时间:2024/05/17 04:14

用过啥啥助手的都对一个悬浮球有印象,你可以任意拖拽这个悬浮窗口,点击它会展开很多功能菜单。

这里给出一个用传统setFrame的方法实现的例子,效果如下:

           

#import <UIKit/UIKit.h>@interface SuspensionFrameController : UIViewController@property (nonatomic, strong) UIView* flowMenu;//悬浮按钮@property (nonatomic, strong) UIButton* flowButton;//菜单栏@property (nonatomic, assign) BOOL isFlowButtonDismiss;    //纪录悬浮框是否被隐藏@property (nonatomic, strong) NSTimer* hideFlowButtonTimer; //控制半隐藏的计时器@end

实现文件:

#import "QQChating.h"#import "SuspensionFrameController.h"@interface SuspensionFrameController ()@end@implementation SuspensionFrameController- (void)viewDidLoad{    [super viewDidLoad];        UIButton* showButton = [UIButton buttonWithType:UIButtonTypeCustom];    [showButton setFrame:CGRectMake(0, 0, 40, 40)];    [showButton setCenter:self.view.center];    [showButton setBackgroundColor:[UIColor blackColor]];    [showButton setTitle:@"点" forState:UIControlStateNormal];    [showButton addTarget:self action:@selector(showButtonClicked:)         forControlEvents:UIControlEventTouchUpInside];    [self.view addSubview:showButton];        //默认悬浮框为半隐藏态    _isFlowButtonDismiss = YES;    }- (void)didReceiveMemoryWarning{    [super didReceiveMemoryWarning];}- (void)showButtonClicked:(id)sender{    //为什么判断nil,因为防止切换账号继续登录,切换账号之前已经存在这两个对象了    if (_flowButton == nil)    {        /** 菜单 **/        _flowMenu = [[UIView alloc] init];        [_flowMenu setBounds:CGRectMake(0, 0, 223, 24)];        //初始状态的时候高度定位中间        [_flowMenu setCenter:CGPointMake( VIEW_W(_flowMenu) * 0.5f + 18, VIEW_H(self.view) * 0.5f)];        [_flowMenu setAutoresizesSubviews:YES];        [_flowMenu setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin |                                       UIViewAutoresizingFlexibleLeftMargin |                                       UIViewAutoresizingFlexibleBottomMargin |                                       UIViewAutoresizingFlexibleRightMargin];                UIButton* fristButton = [UIButton buttonWithType:UIButtonTypeCustom];        [fristButton setFrame:CGRectMake(0, 0, 61, VIEW_H(_flowMenu))];        [fristButton setTitle:@"第一栏" forState:UIControlStateNormal];        [fristButton setTitleColor:UIColorWithRGB(0x88, 0x4a, 0x0e)                              forState:UIControlStateNormal];        [fristButton setTitleColor:[UIColor whiteColor]                              forState:UIControlStateSelected];        [fristButton.titleLabel setFont:[UIFont systemFontOfSize:9]];        [fristButton setBackgroundImage:[UIImage imageNamed:@"firstC.png"]                                   forState:UIControlStateNormal];        [fristButton setBackgroundImage:[UIImage imageNamed:@"firstC_seleted.png"]                                   forState:UIControlStateSelected];        [fristButton addTarget:self action:@selector(fristButtonClicked:)                  forControlEvents:UIControlEventTouchUpInside];                UIButton* secondButton = [UIButton buttonWithType:UIButtonTypeCustom];        [secondButton setFrame:CGRectMake(VIEW_W(fristButton), 0, 55, VIEW_H(_flowMenu))];        [secondButton setTitle:@"第二栏" forState:UIControlStateNormal];        [secondButton setTitleColor:UIColorWithRGB(0x88, 0x4a, 0x0e)                           forState:UIControlStateNormal];        [secondButton setTitleColor:[UIColor whiteColor] forState:UIControlStateSelected];        [secondButton.titleLabel setFont:[UIFont systemFontOfSize:9]];        [secondButton setBackgroundImage:[UIImage imageNamed:@"secondC.png"]                                forState:UIControlStateNormal];        [secondButton setBackgroundImage:[UIImage imageNamed:@"secondC_seleted.png"]                                forState:UIControlStateSelected];        [secondButton addTarget:self action:@selector(secondButtonClicked:)               forControlEvents:UIControlEventTouchUpInside];                UIButton* thirdButton = [UIButton buttonWithType:UIButtonTypeCustom];        [thirdButton setFrame:CGRectMake(VIEW_TLX(secondButton) + VIEW_W(secondButton), 0, 55, VIEW_H(_flowMenu))];        [thirdButton setTitle:@"第三栏" forState:UIControlStateNormal];        [thirdButton setTitleColor:UIColorWithRGB(0x88, 0x4a, 0x0e)                          forState:UIControlStateNormal];        [thirdButton setTitleColor:[UIColor whiteColor]                          forState:UIControlStateSelected];        [thirdButton.titleLabel setFont:[UIFont systemFontOfSize:9]];        [thirdButton setBackgroundImage:[UIImage imageNamed:@"thirdC.png"]                               forState:UIControlStateNormal];        [thirdButton setBackgroundImage:[UIImage imageNamed:@"thirdC_seleted.png"]                               forState:UIControlStateSelected];        [thirdButton addTarget:self action:@selector(thirdButtonClicked:)              forControlEvents:UIControlEventTouchUpInside];                UIButton* forthButton = [UIButton buttonWithType:UIButtonTypeCustom];        [forthButton setFrame:CGRectMake(VIEW_TLX(thirdButton) + VIEW_W(thirdButton), 0, 55, VIEW_H(_flowMenu))];        [forthButton setTitle:@"第四栏" forState:UIControlStateNormal];        [forthButton setTitleColor:UIColorWithRGB(0x88, 0x4a, 0x0e)                          forState:UIControlStateNormal];        [forthButton setTitleColor:[UIColor whiteColor]                          forState:UIControlStateSelected];        [forthButton.titleLabel setFont:[UIFont systemFontOfSize:9]];        [forthButton setBackgroundImage:[UIImage imageNamed:@"forthC.png"]                               forState:UIControlStateNormal];        [forthButton setBackgroundImage:[UIImage imageNamed:@"forthC_seleted.png"]                               forState:UIControlStateSelected];        [forthButton addTarget:self action:@selector(forthButtonClicked:)              forControlEvents:UIControlEventTouchUpInside];                [_flowMenu addSubview:fristButton];        [_flowMenu addSubview:secondButton];        [_flowMenu addSubview:thirdButton];        [_flowMenu addSubview:forthButton];                [self.view addSubview:_flowMenu];                //初始态隐藏        [_flowMenu setHidden:YES];                /** 处理洋葱头按钮 **/        _flowButton = [UIButton buttonWithType:UIButtonTypeCustom];        [_flowButton setFrame:CGRectMake(0, 0, 36, 36)];        [_flowButton setCenter:CGPointMake(VIEW_W(_flowButton) * 0.5f, _flowMenu.center.y - 10)];        [_flowButton setAutoresizesSubviews:YES];        [_flowButton setAutoresizingMask:UIViewAutoresizingFlexibleTopMargin |                                         UIViewAutoresizingFlexibleLeftMargin |                                         UIViewAutoresizingFlexibleBottomMargin |                                         UIViewAutoresizingFlexibleRightMargin];        [_flowButton setImage:[UIImage imageNamed:@"unselected.png"] forState:UIControlStateNormal];        [_flowButton setImage:[UIImage imageNamed:@"selected.png"] forState:UIControlStateSelected];        [_flowButton addTarget:self action:@selector(flowButtonClicked:)              forControlEvents:UIControlEventTouchUpInside];        [self.view addSubview:_flowButton];                UIPanGestureRecognizer* dragGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(flowButtonHandlePanGesture:)];        dragGesture.minimumNumberOfTouches  = 1;        dragGesture.maximumNumberOfTouches  = 1;        //在悬浮按钮上添加的手势操作        [_flowButton addGestureRecognizer:dragGesture];    }    else    {        [self.view addSubview:_flowMenu];        [self.view addSubview:_flowButton];    }}#pragma mark - 菜单栏Button操作./** 四个菜单栏目 **/- (void)fristButtonClicked:(id)sender{    NSLog(@"fristButtonClicked !");}- (void)secondButtonClicked:(id)sender{    NSLog(@"secondButtonClicked !");}- (void)thirdButtonClicked:(id)sender{    NSLog(@"thirdButtonClicked !");}- (void)forthButtonClicked:(id)sender{    NSLog(@"forthButtonClicked !");}/** 悬浮按钮点击 **/- (void)flowButtonClicked:(id)sender{    NSLog(@"flowButton Clicked !");        if (_isFlowButtonDismiss)    {        //为啥这样判断,因为 : 悬浮框半隐藏的时候        if (_flowButton.center.x < VIEW_W(_flowButton.superview))        {            [_flowButton setFrame:CGRectMake(0, VIEW_TLY(_flowButton),                                             VIEW_W(_flowButton), VIEW_H(_flowButton))];        }        else        {            [_flowButton setFrame:CGRectMake(VIEW_W(_flowButton.superview) - VIEW_W(_flowButton),                                             VIEW_TLY(_flowButton), VIEW_W(_flowButton), VIEW_H(_flowButton))];        }                _isFlowButtonDismiss = NO;    }    UIButton* button = (UIButton*)sender;    [button setSelected:!button.isSelected];    if (button.isSelected)    {        CGPoint v;                if (_flowButton.center.x >= VIEW_W(_flowButton.superview) * 0.5f)        {            v = CGPointMake(-( VIEW_W(_flowButton)*0.5f + VIEW_W(_flowMenu)*0.5f - 5 ), 5);        }        else        {            v = CGPointMake( VIEW_W(_flowButton)*0.5f + VIEW_W(_flowMenu)*0.5f - 5, 5);        }                [_flowMenu setCenter:cgpAdd(_flowButton.center, v)];        [_flowMenu setHidden:NO];                //展开菜单栏,并使计时器暂时失效        [_hideFlowButtonTimer invalidate];    }    else    {        [_flowMenu setHidden:YES];        [self moveFlowButton];    }}/** 移动到半隐藏状态 **/- (void)moveFlowButton{    //每一次移动到半隐藏状态都将所有设置成初始状态,这样才统一    //这个问题遇到过几次了,这个实质上和重用出错也是一个道理,重用也是因为没有兼顾到新的变化嘛    //这个时候只要都把它们设置成原来的样子就像重用的时候重新设置一样        if (_isFlowButtonDismiss == NO)    {        _isFlowButtonDismiss = !_isFlowButtonDismiss;    }        if (_flowMenu.hidden == NO)    {        _flowMenu.hidden = !_flowMenu.hidden;    }        if (_flowButton.isSelected)    {        [_flowButton setSelected:!_flowButton.isSelected];    }        //半隐藏屏幕边缘定时器  :  NSTimer的初始化 , 要分别了解初始化的区别    //这里用了定时器来控制两个节奏: 首先通过下面的代码来使得悬浮框靠边,再触发定时器来半隐藏定时器    _hideFlowButtonTimer = [NSTimer timerWithTimeInterval:4.0f target:self                                                 selector:@selector(hideFlowButton)                                                 userInfo:nil repeats:NO];    float durationTime = VIEW_W(_flowButton.superview) * 0.00125f;        //中线以左 : 左边 中线以右或者中线则靠右    if (_flowButton.center.x < VIEW_W(_flowButton.superview)*0.5f)    {        [UIView animateWithDuration:durationTime animations:^{                    [_flowButton setCenter:CGPointMake(VIEW_W(_flowButton)*0.5f, _flowButton.center.y)];            [_flowMenu setCenter:CGPointMake(VIEW_W(_flowButton) + VIEW_W(_flowMenu)*0.5f, _flowMenu.center.y - 5)];                            } completion:^(BOOL finished){                        //结束靠边,半隐藏            [_hideFlowButtonTimer fire];                                 }];    }    else    {        [UIView animateWithDuration:durationTime animations:^{                    [_flowButton setCenter:CGPointMake(VIEW_W(_flowButton.superview) - VIEW_W(_flowButton)*0.5f + 0.1, _flowButton.center.y)];            [_flowMenu setCenter:CGPointMake(VIEW_W(_flowButton.superview) - VIEW_W(_flowButton) - VIEW_W(_flowMenu)*0.5f + 5, _flowMenu.center.y)];                } completion:^(BOOL finished){                    [_hideFlowButtonTimer fire];                }];    }    }/** 定时器控制的半隐藏悬浮框 **/- (void)hideFlowButton{    if (_flowButton.center.x < VIEW_W(_flowButton.superview)*0.5f)    {        [UIView animateWithDuration:0.5f animations:^{                        [_flowButton setFrame:CGRectMake(-VIEW_W(_flowButton) + 10, VIEW_TLY(_flowButton),                                             VIEW_W(_flowButton), VIEW_H(_flowButton))];                    } completion:^(BOOL finished){                        _isFlowButtonDismiss = YES;        }];    }    else    {        [UIView animateWithDuration:0.5f animations:^{                        [_flowButton setFrame:CGRectMake(VIEW_W(_flowButton.superview) - 10, VIEW_TLY(_flowButton),                                             VIEW_W(_flowButton), VIEW_H(_flowButton))];                    } completion:^(BOOL finished){                    _isFlowButtonDismiss = YES;        }];    }}/**   *  手势操作 - pan : 平移  *  手势操作实现原理 由 : begin changed end三个状态来实现     其中end 状态下如果没有弹出菜单栏直接调用 moveFlowButton 方法 , 如果弹出菜单栏则return , 单纯移动悬浮框      真正要理解这个,是要理解分次调用的时机,触发的情况,例如changed情况下如果变化很小,这个时候return没有操作     但是,还会是调用ended所以就可以看到这个时候先弹出全框再潜入半隐藏 */- (void)flowButtonHandlePanGesture:(UIPanGestureRecognizer* )gesture{    //这里的static用得实在是妙 , 因为这个拉拽是一个持续的过程,用static来保持最妥当    //因为通过移动按钮,所以origin这个点就是按钮的开始状态的坐标    static CGPoint originCenter;    static CGPoint menuOriginCenter;        if (gesture.state == UIGestureRecognizerStateBegan)    {        originCenter = gesture.view.center;        menuOriginCenter = _flowMenu.center;    }    else if (gesture.state == UIGestureRecognizerStateChanged)    {        //注意到,手势是添加在悬浮button上,但是可以通过translationInview取到superview的坐标点        //这个是转化后移动变化的坐标点哦,既变化差,所以直接相加啦  , 思考一下, 为什么不用 [gesture translationInView:gesture.view] ?        CGPoint nowCenter = cgpAdd(originCenter, [gesture translationInView:gesture.view.superview]);                //移动很少,连半边脸都没有移够        //移动超多,到达了对面的边        //下面两个没啥必要的        if (nowCenter.x < VIEW_W(_flowButton)*0.5f ||            nowCenter.x > gesture.view.superview.bounds.size.width - VIEW_W(_flowButton)*0.5f ||            nowCenter.y < VIEW_H(_flowButton)*0.5f ||            nowCenter.y > gesture.view.superview.bounds.size.height - VIEW_H(_flowButton)*0.5f)        {            //符合条件的直接返回,但是不是说结束了哦,因为end状态还会触发函数执行            return;        }                //将悬浮框实时移动        gesture.view.center = nowCenter;        CGPoint menuNowCenter = cgpAdd(menuOriginCenter, [gesture translationInView:gesture.view.superview]);        [_flowMenu setCenter:menuNowCenter];    }    else if (gesture.state == UIGestureRecognizerStateEnded)    {        if (_flowMenu.hidden == NO)        {            return;        }        [self moveFlowButton];    }}@end


注:有一些是宏定义的方法在 我博客的编程技巧里面有。

0 0
原创粉丝点击