標簽雲
// // 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
最後更新:2017-04-03 05:39:53