思路:传入一个底层的view,将悬浮按钮(用view实现)和展开的子按钮列表add在其上,子按钮列表开始将坐标和悬浮按钮对应好后先将其隐藏,悬浮按钮识别到tap手势后触发展示子按钮列表的方法。通过在touchMove中实现子按钮列表和悬浮按钮的中心坐标同步更新,实现同时一起拖动,其中限定了悬浮按钮在手指拿起后的坐标,通过在touchEnd作判断调整。关键点是tap手势有时会将touchBegan之后的方法砍断,在调用tap的回调函数后会执行touchCancel方法,所以tap的回调函数中必须带上touchEnd中的功能,将UI的效果恢复和位置纠正。
源码:
#import "SuspendedButton.h" #import <QuartzCore/QuartzCore.h> #define BUTTON_AVAILABLE @"BUTTONAVAILABLE" @interface SuspendedButton () { BOOL _isShowingButtonList; BOOL _isOnLeft; BOOL _isBackUp; BOOL _isTest; CGPoint _tempCenter; CGPoint _originalPosition; CGRect _windowSize; UIView *_buttonListView; UIView *_baseView; } @property (nonatomic,retain) UIView *buttonListView; @property (nonatomic,retain) UIView *baseView; @end @implementation SuspendedButton @synthesize buttonListView = _buttonListView; @synthesize baseView = _baseView; static SuspendedButton *_instance = nil; #pragma mark - 继承方法 - (id)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { _isShowingButtonList = NO; //_isBackUp = NO; //self.hidden = YES; //_isTest = NO; //[self httpRequest]; [[NSNotificationCenter defaultCenter] removeObserver:self name:BUTTON_AVAILABLE object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showButton) name:BUTTON_AVAILABLE object:nil]; } return self; } - (void)dealloc { [_buttonListView release]; [_baseView release]; [[NSNotificationCenterdefaultCenter] removeObserver:self]; [super dealloc]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //NSLog(@"touchBegan"); _originalPosition = [[touches anyObject] locationInView:self]; _tempCenter = self.center; self.backgroundColor = [UIColor blueColor]; CGAffineTransform toBig = CGAffineTransformMakeScale(1.2, 1.2); [UIView animateWithDuration:0.1 animations:^{ // Translate bigger self.transform = toBig; } completion:^(BOOL finished) {}]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { //NSLog(@"touchMove"); CGPoint currentPosition = [[touches anyObject] locationInView:self]; float detaX = currentPosition.x - _originalPosition.x; float detaY = currentPosition.y - _originalPosition.y; CGPoint targetPositionSelf = self.center; CGPoint targetPositionButtonList = _buttonListView.center; targetPositionSelf.x += detaX; targetPositionSelf.y += detaY; targetPositionButtonList.x += detaX; targetPositionButtonList.y += detaY; self.center = targetPositionSelf; _buttonListView.center = targetPositionButtonList; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { //NSLog(@"touchCancell"); // 触发touchBegan后,tap手势被识别后会将touchMove和touchEnd的事件截取掉触发自身手势回调,然后运行touchCancell。touchBegan中设置的按钮状态在touchEnd和按钮触发方法两者中分别设置还原。 } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { //NSLog(@"touchEnd"); CGAffineTransform toNormal = CGAffineTransformTranslate(CGAffineTransformIdentity, 1/1.2, 1/1.2); CGPoint newPosition = [self correctPosition:self.frame.origin]; [UIView animateWithDuration:0.1 animations:^{ // Translate normal self.transform = toNormal; self.backgroundColor = [UIColor greenColor]; } completion:^(BOOL finished) { [UIView animateWithDuration:0.4 animations:^{ self.frame = CGRectMake(newPosition.x, newPosition.y, self.bounds.size.width, self.bounds.size.height); [self adjustButtonListView]; }]; }]; } #pragma mark - 类方法 + (SuspendedButton *)suspendedButtonWithCGPoint:(CGPoint)pos inView:(UIView *)baseview { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[SuspendedButton alloc] initWithCGPoint:pos]; _instance.baseView = baseview; [_instance constructUI]; [baseview addSubview:_instance]; [_instance release]; }); return _instance; } + (void)deleteSuspendedButton { [_instance removeFromSuperview]; } #pragma mark - 辅助方法 - (id)initWithCGPoint:(CGPoint)pos { _windowSize = [SDKFunctionLib rectFromWinSize_CurrentWinSize]; //封装了获取屏幕Size的方法 CGPoint newPosition = [self correctPosition:pos]; return [self initWithFrame:CGRectMake(newPosition.x, newPosition.y, 50, 50)]; } - (CGPoint)correctPosition:(CGPoint)pos { CGPoint newPosition; if ((pos.x + 50 > _windowSize.size.width) || (pos.x > _windowSize.size.width/2-25)) { newPosition.x = _windowSize.size.width - 50; _isOnLeft = NO; } else { newPosition.x = 0; _isOnLeft = YES; } (pos.y + 50 > _windowSize.size.height)?(newPosition.y = _windowSize.size.height - 50):((pos.y < 0)?newPosition.y = 0:(newPosition.y = pos.y)); return newPosition; } - (void)constructUI { self.backgroundColor = [UIColor greenColor]; self.alpha = 0.6; self.layer.cornerRadius = 10; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tiggerButtonList)]; tap.numberOfTapsRequired = 1; tap.numberOfTouchesRequired = 1; [self addGestureRecognizer:tap]; [tap release]; NSUInteger numOfButton = 4; self.buttonListView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, numOfButton*40, 40)] autorelease]; _buttonListView.backgroundColor = [UIColor blueColor]; _buttonListView.alpha = 0; _buttonListView.layer.cornerRadius = 10; [self createButtonByNumber:numOfButton withSize:CGSizeMake(40, 40) inView:(UIView *)_buttonListView]; _buttonListView.hidden = YES; [_baseView addSubview:_buttonListView]; } - (void)createButtonByNumber:(NSUInteger)number withSize:(CGSize)size inView:(UIView *)view { //子按钮的UI效果自定义 for (NSUInteger i = 0; i < number; i++) { UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button addTarget:self action:@selector(optionsButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; button.tag = i + 2000; button.frame = CGRectMake(0 + i*size.width, 0, size.width, size.height); button.tintColor = [UIColor redColor]; [view addSubview:button]; } } - (void)adjustButtonListView { if (_isOnLeft) { _buttonListView.frame = CGRectMake(50, self.center.y - 20, _buttonListView.bounds.size.width, _buttonListView.bounds.size.height); } else { _buttonListView.frame = CGRectMake((_windowSize.size.width - 50 - _buttonListView.bounds.size.width), self.center.y - 20, _buttonListView.bounds.size.width, _buttonListView.bounds.size.height); } } #pragma mark - 按钮回调 - (void)tiggerButtonList { //NSLog(@"tiggerTap"); _isShowingButtonList = !_isShowingButtonList; CGAffineTransform toNormal = CGAffineTransformTranslate(CGAffineTransformIdentity, 1/1.2, 1/1.2); [UIView animateWithDuration:0.1 animations:^{ // Translate normal self.transform = toNormal; self.backgroundColor = [UIColor greenColor]; } completion:^(BOOL finished) { [UIView animateWithDuration:0.1 animations:^{ self.center = _tempCenter; [self adjustButtonListView]; } completion:^(BOOL finished) { [UIView animateWithDuration:0.5 animations:^{ _buttonListView.hidden = !_isShowingButtonList; _isShowingButtonList ? (_buttonListView.alpha = 0.6) : (_buttonListView.alpha = 0); }]; }]; }]; } - (void)optionsButtonPressed:(UIButton *)button { //NSLog(@"buttonNumberPressed:%d",button.tag); switch (button.tag) { case 0: break; case 1: break; case 2: break; case 3: break; default: break; } } #pragma mark - 网络请求方法 -(void)httpRequest { NSMutableString *tempURL = [[NSMutableString alloc] init]; NSString *requesURL = URL_SUSPENDED_BUTTON; //自定义需求的目标url [UIApplication sharedApplication].networkActivityIndicatorVisible=YES; //封装了网络请求的方法 [HttpRequest getRequestWithURLString:tempURL isSynchronism:NO andFinishHandle:^(NSData *data, NSError *error) { if (error) { if (!_isBackUp) { _isBackUp = YES; [self httpRequest]; } else { _isBackUp = NO; [UIApplication sharedApplication].networkActivityIndicatorVisible=NO; } } else { _isBackUp = NO; NSDictionary *requestDic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:Nil]; NSString *code = [requestDic objectForKey:@"code"]; if ([code isEqualToString:@"1000"]) { [[NSNotificationCenter defaultCenter] postNotificationName:BUTTON_AVAILABLE object:nil]; } } }]; [tempURL release]; } #pragma mark - 网络请求回调 - (void)showButton { self.hidden = NO; } @end
效果: