// // HYBCloudView.h // CloudShopping // // Created by ljy-335 on 14-8-11. // Copyright (c) 2014年 uni2uni. All rights reserved. // #import <UIKit/UIKit.h> #import <objc/runtime.h> @protocol HYBCloudViewDelegate <NSObject> - (void)onCloudViewTagClickedWithText:(NSString *)text; @end /*! * @brief 标签云效果视图定制 * @create date 2014-08-11 * @author huangyibiao */ @interface HYBCloudView : UIView @property (nonatomic, weak) id<HYBCloudViewDelegate> delegate; @property (nonatomic, assign) NSUInteger tagCounts; /*! * @brief 刷新所有标签值 * @param tagsArray 标签值数组 */ - (void)reloadCloudTagsWithTags:(NSArray *)tagsArray; @end //-----------------------------------------------------------------------------// // @brief 浮动标签定制 // @author huangyibiao //-----------------------------------------------------------------------------// @interface HYBBubbleLabel : UILabel @property (nonatomic, weak) id delegate; @end //-----------------------------------------------------------------------------// // @brief NSObject 扩展 // @author huangyibiao //-----------------------------------------------------------------------------// @interface NSObject (KVC) - (id)valueForUndefinedKey:(NSString *)key; - (void)setValue:(id)value forUndefinedKey:(NSString *)key; @end
// // HYBCloudView.m // CloudShopping // // Created by ljy-335 on 14-8-11. // Copyright (c) 2014年 uni2uni. All rights reserved. // #import "HYBCloudView.h" #define kBubbleGapX 35 #define kBubbleGapY 10 #define kFontHeight 35 #define kDuration 16 #define kFlowInterval 2 #define kFont [UIFont systemFontOfSize:16] @interface HYBCloudView () { NSMutableArray *_datasources; NSInteger _currentIndex; } @end @implementation HYBCloudView // 点下标签暂停动画 - (void)onBubbleTouchDown:(HYBBubbleLabel *)sender { CFTimeInterval pausedTime = [sender.layer convertTime:CACurrentMediaTime() fromLayer:nil]; sender.layer.speed = 0.0; sender.layer.timeOffset = pausedTime; NSLog(@"暂停动画"); return; } // 点击标签 - (void)onBubbleTouchUpInside:(HYBBubbleLabel *)sender { NSLog(@"点击了"); if ([self.delegate respondsToSelector:@selector(onCloudViewTagClickedWithText:)]) { [self.delegate onCloudViewTagClickedWithText:sender.text]; } [self performSelector:@selector(onBubbleTouchUpOutside:) withObject:sender afterDelay:0.4]; return; } // 点离标签继续动画 - (void)onBubbleTouchUpOutside:(HYBBubbleLabel *)sender { CFTimeInterval pausedTime = [sender.layer timeOffset]; sender.layer.speed = 1.0; sender.layer.timeOffset = 0.0; sender.layer.beginTime = 0.0; CFTimeInterval timeSincePause = [sender.layer convertTime:CACurrentMediaTime() fromLayer:nil]; timeSincePause -= pausedTime; sender.layer.beginTime = timeSincePause; NSLog(@"点离了"); return; } // 根据界面宽度选择显示的标签,最多选择3个 - (NSArray *)selectAtMostThreeBubbles { NSInteger tempIndex = _currentIndex; NSString *ob1 = [self objectArray:_datasources atIndex:tempIndex]; CGFloat width1 = [ob1 sizeWithFont:kFont constrainedToSize:CGSizeMake(1000, kFontHeight)].width; ++tempIndex; NSString *ob2 = [self objectArray:_datasources atIndex:tempIndex]; CGFloat width2 = [ob2 sizeWithFont:kFont constrainedToSize:CGSizeMake(1000, kFontHeight)].width; ++tempIndex; NSString *ob3 = [self objectArray:_datasources atIndex:tempIndex]; CGFloat width3 = [ob3 sizeWithFont:kFont constrainedToSize:CGSizeMake(1000, kFontHeight)].width; if (width1 + width2 + width3 > 280) {// 如果三条显示的长度太长 就取两条显示 //如果两条显示的长度太长 就取一条显示 if (width1 + width2 > 280) { ++_currentIndex; return @[ob1]; } else { _currentIndex += 2; return @[ob1, ob2]; } } _currentIndex += 3; return @[ob1, ob2, ob3]; } // 添加动画 - (NSArray *)bubbleArrayAnimatedUp { _currentIndex %= _datasources.count; NSArray *tempArray = [self selectAtMostThreeBubbles]; NSMutableArray *resultArray = [[NSMutableArray alloc] init]; for (int i = 0; i < tempArray.count; i++) { NSString *text = [tempArray objectAtIndex:i]; CGFloat width = [text sizeWithFont:kFont constrainedToSize:CGSizeMake(1000, kFontHeight)].width; HYBBubbleLabel *label = [[HYBBubbleLabel alloc] init]; label.frame = CGRectMake(self.width / 2 - width / 2, self.height - kFontHeight, width, kFontHeight); label.text = text; label.delegate = self; label.font = kFont; [label setTextColor:[UIColor whiteColor]]; label.backgroundColor = [UIColor clearColor]; [self addSubview:label]; [resultArray addObject:label]; CGFloat width0 = [[tempArray objectAtIndex:0] sizeWithFont:kFont constrainedToSize:CGSizeMake(1000, kFontHeight)].width; CGRect rect = label.frame; // 如果只有两个,那么左边一个、右边一个 if ([tempArray count] == 2) { if (i == 0) { // 左边的 rect.origin.x = self.width / 4 - width / 2; } else if (i == 1) {// 右边的 rect.origin.x = self.width / 4.0 * 3.0 - width / 2; } rect.origin.y -= kFontHeight / 2; } else {// 如果是有1个或者3个 if (i == 1) { rect.origin.x = self.width / 2 - width0 / 2; rect.origin.x -= (width - kBubbleGapX); } else if (i == 2) { rect.origin.x = self.width / 2 - width0 / 2; rect.origin.x += width0 - kBubbleGapX; } rect.origin.y -= (kFontHeight - kBubbleGapY); } label.frame = rect; CABasicAnimation *basicScale = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; basicScale.fromValue = [NSNumber numberWithFloat:.65]; basicScale.toValue = [NSNumber numberWithFloat:1.]; basicScale.autoreverses = YES; basicScale.duration = kDuration / 2; basicScale.fillMode = kCAFillModeForwards; basicScale.removedOnCompletion = NO; basicScale.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [label.layer addAnimation:basicScale forKey:@"s"]; CABasicAnimation *basicOpa = [CABasicAnimation animationWithKeyPath:@"opacity"]; basicOpa.fromValue = [NSNumber numberWithFloat:0.3]; basicOpa.toValue = [NSNumber numberWithFloat:1.]; basicOpa.autoreverses = YES; basicOpa.duration = kDuration / 2; basicOpa.fillMode = kCAFillModeForwards; basicOpa.removedOnCompletion = NO; basicOpa.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [label.layer addAnimation:basicOpa forKey:@"o"]; UIBezierPath *path=[UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(label.originX + width / 2, label.originY + kFontHeight / 2)]; switch (i) { case 0: { if ([tempArray count] == 1 || [tempArray count] == 3) {// 此时中间的走直线 [path addLineToPoint:CGPointMake(label.originX + width / 2, kFontHeight / 2)]; } else if ([tempArray count] == 2) {// 没有中间 走左弧 [path addCurveToPoint:CGPointMake(label.originX + width / 2, kFontHeight / 2) controlPoint1:CGPointMake(label.originX + width / 2, label.originY + kFontHeight / 2) controlPoint2:CGPointMake(0, self.height / 2)]; } } break; case 1: { if ([tempArray count] == 3) {// i==1 走左弧 [path addCurveToPoint:CGPointMake(label.originX + width / 2, kFontHeight / 2) controlPoint1:CGPointMake(label.originX + width / 2, label.originY + kFontHeight / 2) controlPoint2:CGPointMake(0, self.height / 2)]; } else if ([tempArray count] == 2) {// 走右弧 [path addCurveToPoint:CGPointMake(label.originX + width / 2, kFontHeight / 2) controlPoint1:CGPointMake(label.originX + width / 2, label.originY + kFontHeight / 2) controlPoint2:CGPointMake(self.width, self.height / 2)]; } } break; case 2: [path addCurveToPoint:CGPointMake(label.originX + width / 2, kFontHeight / 2) controlPoint1:CGPointMake(label.originX + width / 2, label.originY + kFontHeight / 2) controlPoint2:CGPointMake(self.width, self.height / 2)]; break; } CAKeyframeAnimation *keyPosition = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyPosition.path = path.CGPath; keyPosition.fillMode = kCAFillModeForwards; keyPosition.removedOnCompletion = NO; keyPosition.duration = kDuration; keyPosition.delegate = self; [keyPosition setValue:label forKeyPath:@"itslayer"]; keyPosition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [label.layer addAnimation:keyPosition forKey:@"x"]; } return resultArray; } #pragma mark - CAAnimationDelegate - (void)animationDidStart:(CAAnimation *)anim { UIView *view = [anim valueForKeyPath:@"itslayer"]; NSNumber *number = [view valueForKeyPath:@"timeoffset"]; if (number != nil) { view.layer.timeOffset = number.floatValue; } return; } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if (flag) { UIView *view = [anim valueForKeyPath:@"itslayer"]; [view removeFromSuperview]; } return; } - (NSUInteger)tagCounts { return _datasources.count; } #pragma mark - 更新数据 - (void)reloadCloudTagsWithTags:(NSArray *)tagsArray { if (tagsArray.count == 0) { return; } if (_datasources == nil) { _datasources = [[NSMutableArray alloc] init]; } [_datasources removeAllObjects]; [_datasources addObjectsFromArray:tagsArray]; // 设置初始动画偏移量 for (int i = 0; i < kDuration / kFlowInterval; i++) { NSArray *bubbleArray = [self bubbleArrayAnimatedUp]; for (UIView *view in bubbleArray) { [view setValue:[NSNumber numberWithFloat:(i + 1) * kFlowInterval] forKeyPath:@"timeoffset"]; } } NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:kFlowInterval target:self selector:@selector(bubbleArrayAnimatedUp) userInfo:nil repeats:YES]; [timer fire]; return; } - (id)objectArray:(NSArray *)objectArray atIndex:(NSUInteger)index { return [objectArray objectAtIndex:(index % objectArray.count)]; } - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ for (UIView *view in self.subviews) { if (CGRectContainsPoint(((CALayer *)view.layer.presentationLayer).frame, point)) { return view; } } return nil; } @end //-----------------------------------------------------------------------------// // @brief 浮动标签定制 // @author huangyibiao //-----------------------------------------------------------------------------// @implementation HYBBubbleLabel - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesBegan:touches withEvent:event]; if ([self.delegate respondsToSelector:@selector(onBubbleTouchDown:)]) { [self.delegate performSelector:@selector(onBubbleTouchDown:) withObject:self]; } return; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesMoved:touches withEvent:event]; return; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesEnded:touches withEvent:event]; UITouch *touch = [touches anyObject]; if (CGRectContainsPoint([(CALayer *)[self.layer presentationLayer] frame], [touch locationInView:self.superview])) { if ([self.delegate respondsToSelector:@selector(onBubbleTouchUpInside:)]) { [self.delegate performSelector:@selector(onBubbleTouchUpInside:) withObject:self]; } } else { if ([self.delegate respondsToSelector:@selector(onBubbleTouchUpOutside:)]) { [self.delegate performSelector:@selector(onBubbleTouchUpOutside:) withObject:self]; } } return; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { [super touchesCancelled:touches withEvent:event]; UITouch *touch = [touches anyObject]; if (CGRectContainsPoint([(CALayer *)[self.layer presentationLayer] frame], [touch locationInView:self.superview])) { if ([self.delegate respondsToSelector:@selector(onBubbleTouchUpInside:)]) { [self.delegate performSelector:@selector(onBubbleTouchUpInside:) withObject:self]; } } else { if ([self.delegate respondsToSelector:@selector(onBubbleTouchUpOutside:)]) { [self.delegate performSelector:@selector(onBubbleTouchUpOutside:) withObject:self]; } } return; } @end //-----------------------------------------------------------------------------// // @brief NSObject 扩展实现 // @author huangyibiao //-----------------------------------------------------------------------------// @implementation NSObject (KVC) - (id)valueForUndefinedKey:(NSString *)key{ return objc_getAssociatedObject(self, (__bridge const void *)(key)); } - (void)setValue:(id)value forUndefinedKey:(NSString *)key{ objc_setAssociatedObject(self, (__bridge const void *)(key), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return; } @end