737
技術社區[雲棲]
標簽雲
// // 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