閑來無事,分享一個最近在某個地方借鑒的一個demo(原諒我真的忘了在哪里看到的了洁奈,不然也就貼地址了)這個demo的邏輯思路并不是很難厢钧,推敲一下滥玷,很快就能理解妇蛀,只是覺得這樣的一個組合控件用起來蠻能增色自己的APP的,所以也就記下了。
先給你們看一下效果圖。
這里的懸浮小球其實是一個組合控件魄缚,可以在上面加上其他效果。之后我會上傳demo。如果要做成QQ語音的那種冶匹,可以把圖片移除习劫,換上一個label,在label上加上時間久可以了嚼隘,用的時候诽里,可以直接把這個類拖進工程,直接加到想要添加的仕途上就可以啦飞蛹。
這個demo我也是學習過程中做了很多注釋谤狡,基本上都應該能看懂,還是一樣不多解釋桩皿,看注釋看代碼豌汇。_
這是還是說一下具體的實現(xiàn)功能吧:
第一個:是營造一個呼吸的效果幢炸,這個呼吸效果實際上只是一個假象泄隔,UI控件哪有什么呼吸的嘛,都是自己YY的宛徊。這個呼吸效果實際上是通過動畫效果改變Alpha佛嬉,不斷循環(huán),達到一致循環(huán)改變Alpha闸天,感覺就像是在呼吸一樣暖呕。
第二個:是圖片效果,這里是采用了UIImageRenderingModeAlwaysTemplate苞氮,不懂得同學可以查一下湾揽,不過我demo里面也有部分解釋。這里主要是忽略的圖片的顏色笼吟,設置成自己想要的顏色(這里的圖片原本是黑色的库物,我通過這個枚舉,讓他的顏色變成了白色)
第三個:是移動贷帮,這里是通過-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 戚揭, -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event以及 - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 這三個方法來實現(xiàn)移動的,大致就是觸摸時取出觸摸點坐標撵枢,然后作為懸浮小球的center民晒,實現(xiàn)了小球的移動。
//將觸摸點賦值給touchView的中心點 也就是根據(jù)觸摸的位置實時修改view的位置
self.center = [startTouch locationInView:self.superview];
還有一個注意的是在開始移動之前一定要把之前的效果給移除掉锄禽,不然會很奇葩的潜必,不信你也可以試一試。
// 移除之前的所有行為
// 如果不移除沃但,之前移動所觸發(fā)的物理吸附事件就會一直執(zhí)行
[self.animator removeAllBehaviors];
第四:就是計算計算距離最近的邊緣 吸附到邊緣凸伪悖靠,具體的計算就不說了绽慈,因為太繁瑣了恨旱,沒有想著優(yōu)化辈毯,看看啥時候閑下來再來看看能不能優(yōu)化吧。主要就是考慮的情況稍微多了點搜贤,但是邏輯上還是很清晰的谆沃。這里有一個吸附的物理行為,其實也就是一個動畫效果仪芒,直接加上就好了唁影,也不是很復雜。
好的掂名,具體的都解釋完了据沈。上代碼!=让铩锌介!,突然發(fā)現(xiàn)并不能傳文件猾警,好吧孔祸,賦值過來看看吧
這是.h里面的代碼
這里只是給外界留了個借口,這些屬性也可以.m里面发皿,我只是在外界用到了這個而已崔慧。
#import <UIKit/UIKit.h>
typedef void (^DownLoadBlock) ();
@interface xuanfuwu : UIView
@property (nonatomic ,assign) CGPoint startPoint;//觸摸起始點
@property (nonatomic ,assign) CGPoint endPoint;//觸摸結(jié)束點
@property (nonatomic ,copy) DownLoadBlock downLoadBlock;
@end
這是.m里面的代碼
//主題顏色
#import "AppDelegate.h"
#import "xuanfuwu.h"
#define MAINCOLOER [UIColor colorWithRed:105/255.0 green:149/255.0 blue:246/255.0 alpha:1]
#define kDownLoadWidth 60
#define kOffSet kDownLoadWidth / 2
@interface xuanfuwu ()<UIDynamicAnimatorDelegate>
@property (nonatomic , retain ) UIView *backgroundView;//背景視圖
@property (nonatomic , retain ) UIImageView *imageView;//圖片視圖
@property (nonatomic , retain ) UIDynamicAnimator *animator;//物理仿真動畫
@end
@implementation xuanfuwu
//初始化
- (instancetype)initWithFrame:(CGRect)frame{
// 設置 xuanfuwu 這個視圖的大小 寬高都是60
frame.size.width = kDownLoadWidth;
frame.size.height = kDownLoadWidth;
if (self = [super initWithFrame:frame]) {
//初始化背景視圖
_backgroundView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.frame), CGRectGetHeight(self.frame))];
// 讓初始化背景變成圓
_backgroundView.layer.cornerRadius = _backgroundView.frame.size.width / 2;
// clipsToBounds
// 是指視圖上的子視圖,如果超出父視圖的部分就截取掉,
// masksToBounds
// 卻是指視圖的圖層上的子圖層,如果超出父圖層的部分就截取掉
_backgroundView.clipsToBounds = YES;
// 設置顏色和透明度
_backgroundView.backgroundColor = [MAINCOLOER colorWithAlphaComponent:0.7];
_backgroundView.userInteractionEnabled = NO;
[self addSubview:_backgroundView];
//初始化圖片背景視圖
// 比背景視圖稍微小一點,顯示出呼吸的效果
UIView * imageBackgroundView = [[UIView alloc]initWithFrame:CGRectMake(5, 5, CGRectGetWidth(self.frame) - 10, CGRectGetHeight(self.frame) - 10)];
// 變圓
imageBackgroundView.layer.cornerRadius = imageBackgroundView.frame.size.width / 2;
imageBackgroundView.clipsToBounds = YES;
imageBackgroundView.backgroundColor = [MAINCOLOER colorWithAlphaComponent:0.8f];
imageBackgroundView.userInteractionEnabled = NO;
[self addSubview:imageBackgroundView];
//初始化圖片
_imageView = [[UIImageView alloc]initWithImage:[[UIImage imageNamed:@"1.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]];
// UIImageRenderingModeAutomatic // 根據(jù)圖片的使用環(huán)境和所處的繪圖上下文自動調(diào)整渲染模式穴墅。
// UIImageRenderingModeAlwaysOriginal // 始終繪制圖片原始狀態(tài)惶室,不使用Tint Color。
// UIImageRenderingModeAlwaysTemplate // 始終根據(jù)Tint Color繪制圖片玄货,忽略圖片的顏色信息皇钞。
_imageView.tintColor = [UIColor whiteColor];
_imageView.frame = CGRectMake(0, 0, 30, 30);
_imageView.center = CGPointMake(kDownLoadWidth / 2 , kDownLoadWidth / 2);
[self addSubview:_imageView];
//將正方形的view變成圓形
self.layer.cornerRadius = kDownLoadWidth / 2;
//開啟呼吸動畫
[self HighlightAnimation];
}
return self;
}
// 觸摸事件的觸摸點
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//得到觸摸點
UITouch *startTouch = [touches anyObject];
//返回觸摸點坐標
self.startPoint = [startTouch locationInView:self.superview];
// 移除之前的所有行為
// 如果不移除,之前移動所觸發(fā)的物理吸附事件就會一直執(zhí)行
[self.animator removeAllBehaviors];
}
//觸摸移動
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
//得到觸摸點
// 這里只有一個手指誉结,移動的坐標只有一個
UITouch *startTouch = [touches anyObject];
//將觸摸點賦值給touchView的中心點 也就是根據(jù)觸摸的位置實時修改view的位置
self.center = [startTouch locationInView:self.superview];
}
//結(jié)束觸摸
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
//得到觸摸結(jié)束點
UITouch *endTouch = [touches anyObject];
//返回觸摸結(jié)束點
// 這個方法是指:觸摸的點時在哪個視圖鹅士,xuanfuwu 是加在viewController上的,所以用superView
self.endPoint = [endTouch locationInView:self.superview];
//判斷是否移動了視圖 (誤差范圍5)
CGFloat errorRange = 5;
if (( self.endPoint.x - self.startPoint.x >= -errorRange && self.endPoint.x - self.startPoint.x <= errorRange ) && ( self.endPoint.y - self.startPoint.y >= -errorRange && self.endPoint.y - self.startPoint.y <= errorRange )) {
// 如果移動范圍不超過 5 惩坑,就不進行物理事件的響應
} else {
//移動
self.center = self.endPoint;
//計算距離最近的邊緣 吸附到邊緣偷糁眩靠
CGFloat superwidth = self.superview.bounds.size.width;
CGFloat superheight = self.superview.bounds.size.height;
CGFloat endX = self.endPoint.x;
CGFloat endY = self.endPoint.y;
CGFloat topRange = endY;//上距離
CGFloat bottomRange = superheight - endY;//下距離
CGFloat leftRange = endX;//左距離
CGFloat rightRange = superwidth - endX;//右距離
//比較上下左右距離 取出最小值
CGFloat minRangeTB = topRange > bottomRange ? bottomRange : topRange;//獲取上下最小距離
CGFloat minRangeLR = leftRange > rightRange ? rightRange : leftRange;//獲取左右最小距離
CGFloat minRange = minRangeTB > minRangeLR ? minRangeLR : minRangeTB;//獲取最小距離
//判斷最小距離屬于上下左右哪個方向 并設置該方向邊緣的point屬性
CGPoint minPoint;
if (minRange == topRange) {
//上
endX = endX - kOffSet < 0 ? kOffSet : endX;
endX = endX + kOffSet > superwidth ? superwidth - kOffSet : endX;
minPoint = CGPointMake(endX , 0 + kOffSet);
} else if(minRange == bottomRange){
//下
endX = endX - kOffSet < 0 ? kOffSet : endX;
endX = endX + kOffSet > superwidth ? superwidth - kOffSet : endX;
minPoint = CGPointMake(endX , superheight - kOffSet);
} else if(minRange == leftRange){
//左
endY = endY - kOffSet < 0 ? kOffSet : endY;
endY = endY + kOffSet > superheight ? superheight - kOffSet : endY;
minPoint = CGPointMake(0 + kOffSet , endY);
} else if(minRange == rightRange){
//右
endY = endY - kOffSet < 0 ? kOffSet : endY;
endY = endY + kOffSet > superheight ? superheight - kOffSet : endY;
minPoint = CGPointMake(superwidth - kOffSet , endY);
}
//添加吸附物理行為
UIAttachmentBehavior *attachmentBehavior = [[UIAttachmentBehavior alloc] initWithItem:self attachedToAnchor:minPoint];
// 吸附行為中的兩個吸附點之間的距離,通常用這個屬性來調(diào)整吸附的長度以舒,可以創(chuàng)建吸附行為之后調(diào)用趾痘。系統(tǒng)基于你創(chuàng)建吸附行為的方法來自動初始化這個長度
[attachmentBehavior setLength:0];
// 阻尼,相當于回彈過程中額阻力效果
[attachmentBehavior setDamping:0.1];
// 回彈的頻率
[attachmentBehavior setFrequency:3];
[self.animator addBehavior:attachmentBehavior];
}
}
#pragma mark ---UIDynamicAnimatorDelegate
- (void)dynamicAnimatorDidPause:(UIDynamicAnimator *)animator{
}
#pragma mark ---LazyLoading
- (UIDynamicAnimator *)animator
{
if (!_animator) {
// 創(chuàng)建物理仿真器(ReferenceView : 仿真范圍)
_animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.superview];
//設置代理
_animator.delegate = self;
}
return _animator;
}
#pragma mark ---BreathingAnimation 呼吸動畫
// 實質(zhì)就是一個循環(huán)動畫蔓钟,透明度來回改變
- (void)HighlightAnimation{
// 修改block塊里面的值
__block typeof(self) Self = self;
[UIView animateWithDuration:1.5f animations:^{
Self.backgroundView.backgroundColor = [Self.backgroundView.backgroundColor colorWithAlphaComponent:0.1f];
} completion:^(BOOL finished) {
[Self DarkAnimation];
}];
}
- (void)DarkAnimation{
__block typeof(self) Self = self;
[UIView animateWithDuration:1.5f animations:^{
Self.backgroundView.backgroundColor = [Self.backgroundView.backgroundColor colorWithAlphaComponent:0.6f];
} completion:^(BOOL finished) {
[Self HighlightAnimation];
}];
}