标签云

//
//  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


你可能感兴趣的:(标签云)