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