不知不覺自學ios已經一個月了,從OC語法到app開發,過程雖然枯燥無味,但是結果還是挺有成就感的,在此分享我的ios開發之路中的小小心得~廢話不多說,先上我們今天要實現的效果圖:
有過一點做APP經驗的都知道,提示框和等待加載框一直是APP首當其中的效果,ios不像android一樣,自帶toast和progressbarDialog,所以在做ios開發的時候,我首先想到了先封裝這兩個基礎控件~當然網上的資源數不勝數,但是博主抱著一顆自主研究的精神,做出的效果也不錯,也已適配了所有iphone型號和版本.望大家多多支持~
YPXToastView實現
接觸過安卓開發的ios開發者可能對待toast這麼個東西很不陌生,它主要是一種輕量級的提示,代替了復雜的對話框,有的顯示在中間,有的顯示在屏幕下方,當然,這些都是根據需求而來的.廢話不多說,首先清理一下我們實現這個toast的一些必要思路:
1.實現的基礎控件------UILabel封裝
2.彈出的時間和透明度變化設置
3.顯示的位置調整
一.UILabel的封裝
首先我們想要實現一下這個效果,首當其沖的肯定想到UILabel,那麼接下來就是對UILabel的封裝了,首先我們創建一文件繼承UIlabel,然後寫好要對外暴露的方法:
@interface YPXToastView : UILabel { @public CGFloat screenWidth,screenHeight; int _corner; int _duration; } @property(assign,nonatomic)int corner; @property(assign,nonatomic)int duration; -(void)showToastViewWithText:(NSString *)text andDuration:(int)duration andParentView:(UIView *)parentView; -(void)showToastViewWithText:(NSString *)text andParentView:(UIView *)parentView; -(void)showToastViewWithText:(NSString *)text andDuration:(int)duration andCorner:(int)corner andParentView:(UIView *)parentView; +(void)showToastViewWithText:(NSString *)text andDuration:(int)duration andParentView:(UIView *)parentView; +(void)showToastViewWithText:(NSString *)text andParentView:(UIView *)parentView; +(void)showToastViewWithText:(NSString *)text andDuration:(int)duration andCorner:(int)corner andParentView:(UIView *)parentView; -(void)setBackgroundWithColor:(UIColor *)color; @end
下面我們來看內部主要方法實現:
/** * 新建UI * * @param str 要顯示的文本 */ -(void)createUIByText:(NSString *)str{ self.textAlignment = NSTextAlignmentCenter; self.backgroundColor = [UIColor colorWithRed:00 green:00 blue:00 alpha:0.5]; self.alpha = 0.8; self.text=str; self.font = [UIFont systemFontOfSize:14]; self.textColor=[UIColor whiteColor]; NSDictionary *attributes = @{NSFontAttributeName:[UIFont systemFontOfSize:self.font.pointSize],}; CGSize textSize = [self.text boundingRectWithSize:CGSizeMake(100, 100) options:NSStringDrawingTruncatesLastVisibleLine attributes:attributes context:nil].size;; self.frame=CGRectMake(screenWidth/2-(textSize.width*1.7)/2, screenHeight*0.5,textSize.width*1.7, textSize.height*2); self.layer.cornerRadius = _corner; self.clipsToBounds = YES; } -(void)setBackgroundWithColor:(UIColor *)color{ self.backgroundColor =color; } /** * 初始化測量數據 */ -(void)caculateSize{ screenWidth=[UIScreen mainScreen].bounds.size.width; screenHeight=[UIScreen mainScreen].bounds.size.height; }
方法一目了然,指定了UILabel的居中方式和背景,並設置屬性讓其寬度自適應,涉及到一些簡單的frame計算,主要是定位於屏幕中間,寬度設為文本寬度的1.7倍,看起來比較適中.y點主要就是屏幕高度的一半,理應減去文本的高度的一半,但是博主在這偷個懶,並沒有計算label的高度,所以就不贅述了~~
二.彈出的時間和透明度變化設置
原理很簡單,就是設定了一個animateWithDuration的block回調,然後設置label的透明度和時間,具體實現如下:
/** * 顯示toast * * @param parentView <#parentView description#> */ -(void)showToastByParentView:(UIView *)parentView{ [parentView addSubview:self]; //animateWithDuration可以控制label顯示持續時間 [UIView animateWithDuration:_duration animations:^{ self.alpha = 1.0; } completion:^(BOOL finished){ [self removeFromSuperview]; }]; }
到此,我們的YPXToastView已經全部完成,其實內部邏輯主要是對UILabel的定制,思路簡單,但是對於ios開發之路的封裝思想有很大的幫助.調用時只需要一行代碼:
[YPXToastView showToastViewWithText:@"已開啟" andDuration:3 andCorner:5 andParentView:self.view];
YPXLoddingView實現
相信在ios的開發中少不了加載等待框的開發,畢竟原生系統中貌似沒有這樣的對話框,我們在訪問網絡或者讀取數據時可能需要給用戶一個等待回饋,這裡就用到了我們的等待加載.上面的gif中提供了兩種等待加載框的樣式,一種是自定義圖片的旋轉,順時針或者逆時針,另一種是使用系統的UIActivityIndicatorView,使用大的加載Loadding.具體開發思路如下:
1.繼承UIView通過添加UIImageView和UILabel來組合實現
2.控制UIImageView的旋轉以及UIlabel的三個點的動態效果
3.顯示和隱藏
一.UIView的封裝
通過效果我們可以一目了然的知道,實現這個控件至少需要一個UIImageView(或者UIActivityIndicatorView)和UILabel,一個提供加載圖片,一個提供加載文本,組合方式為豎直方向,然後設置背景的透明度.具體.h文件如下:
#import@interface YPXLoaddingView : UIView { @public int num; CGFloat angle; BOOL isShowLoadding; UIImageView * imageView; UILabel * label; CGFloat width; CGFloat x; CGFloat y,screenWidth,screenHeight; UIView * _parentView; NSString * _text; NSTimer * _timer; UIActivityIndicatorView * _activityView; UIView * view; } @property(retain,nonatomic)NSTimer * timer; @property(copy,nonatomic) NSString * text; @property(retain,nonatomic) UIActivityIndicatorView * activityView; -(void)showLoaddingViewWithText:(NSString *) string; -(void)dismissLoaddingView; -(instancetype)initWithParentView:(UIView *) parentView; +(id)initWithParentView:(UIView *) parentView; -(BOOL)isShowing; -(void)showLoaddingView; -(void)showLoaddingViewWithStyle:(int)style; -(void)showLoaddingViewWithText:(NSString * )text andStyle:(int)style; @end
/** * 計算一些必要尺寸 * * @param parentView <#parentView description#> */ -(void)caculatSizeWithTarget:(UIView *) parentView { screenWidth=[UIScreen mainScreen].bounds.size.width; screenHeight=[UIScreen mainScreen].bounds.size.height; width=screenWidth*0.3; x= screenWidth/2-width/2; y= screenHeight/2-width/2; angle=0; num=0; isShowLoadding=NO; _parentView=parentView; } /** * 創建loadding視圖 */ -(void)creatLoaddingView { view=[[UIView alloc]init]; view.frame=CGRectMake(0, 0, screenWidth, screenHeight); imageView=[[UIImageView alloc]init]; imageView.frame=CGRectMake(width/2-width*0.5/2,15, width*0.5,width*0.4); imageView.clipsToBounds=YES; imageView.layer.rasterizationScale=[UIScreen mainScreen].scale; [imageView setImage:[UIImage imageNamed:@"loadding.png"]]; _activityView=[[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(width/2-width*0.55/2,15, width*0.55,width*0.45)]; _activityView.activityIndicatorViewStyle=UIActivityIndicatorViewStyleWhiteLarge; label=[[UILabel alloc]init]; label.textColor=[UIColor whiteColor]; label.font=[UIFont systemFontOfSize:14]; int y2=imageView.frame.size.height+(width-imageView.frame.size.height)/2; label.frame=CGRectMake(0,y2, width, 20); label.textAlignment=NSTextAlignmentCenter; }
手動布局,我們指定了imageview和label的frame,通過一系列計算,把imageview設為UIView中上部,並留出四周的邊距,看起來更親切自然一點.label的位置根據imageview的frame來指定,這樣就可以完成適配避免在不同屏幕上顯示不同的問題.完場上述代碼,一個初步的靜態效果已經生成,剩下的就是添加動畫;
二.UIImageView旋轉動畫以及UILabel點點動態展示
imageview的動畫添加很簡單,因為我們只是涉及一點點的旋轉動畫,其中並沒有加速度變化,讀者若是想要添加,可以自己嘗試一下.旋轉動畫的實現方式有兩種:
一種是用animateWithDuration來動態的旋轉一定角度,然後通過延時來改變旋轉的速率,好處是簡單,但是缺點也很明顯,在5s中動畫顯得僵硬,並伴隨著一點點的卡頓,如下是第一種動畫方案的代碼:
/** * 開啟loadding動畫 */ - (void)startAnimation { if(isShowLoadding==YES){ CGAffineTransform endAngle = CGAffineTransformMakeRotation(angle * (M_PI / -180.0f)); [UIView animateWithDuration:0.03f delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ imageView.transform =endAngle; } completion:^(BOOL finished) { if(angle==360){ angle=0; } if(angle==0||angle==360){ label.text=[_text stringByAppendingString:@"..."]; }else if(angle==90){ label.text=_text; }else if(angle==180){ label.text=[_text stringByAppendingString:@"."]; }else if(angle==270){ label.text=[_text stringByAppendingString:@".."]; } angle += 10; [self startAnimation]; }]; } }
通過改變imageview的角度來旋轉圖片的方式,使用block回調中的角度關系,我們可以動態的設置提示文本省略號的動態展示.因為實現效果有點卡頓,所以博主采用了第二種實現方式,代碼如下:
/** * 啟動計數定時器 */ -(void)UpdateText { num++; if (num>4) { num=0; } if(num==0||num==4){ label.text=[_text stringByAppendingString:@"..."]; }else if(num==1){ label.text=_text; }else if(num==2){ label.text=[_text stringByAppendingString:@"."]; }else if(num==3){ label.text=[_text stringByAppendingString:@".."]; } } /** * 給imageView添加動畫 * * @param imageView imageview * * @return imageview */ + (UIImageView *)rotateImageView:(UIImageView *)imageView { CABasicAnimation *animation = [ CABasicAnimation animationWithKeyPath: @"transform" ]; animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity]; //圍繞Z軸旋轉,垂直與屏幕 animation.toValue = [ NSValue valueWithCATransform3D: CATransform3DMakeRotation(M_PI, 0.0, 0.0, 1.0) ]; animation.duration = 0.5; //旋轉效果累計,先轉180度,接著再旋轉180度,從而實現360旋轉 animation.cumulative = YES; animation.repeatCount = 10000; [imageView.layer addAnimation:animation forKey:nil]; return imageView; }
完成了我們的圖片旋轉,基本上這個功能已經完成了百分之八十,剩下就是顯示和隱藏了;
三.顯示和隱藏
前面介紹.h文件申明的時候,已經把本控件的所有調用方法已經列出來了,其中包含了一系列的.show方法,因為loadding這種控件,我們可能需要對其狀態進行判斷,而且可能在網絡請求中調用多次,為了不浪費內存,我們在這裡提倡使用單例模式,並初始化一個Loadding在ViewDidLoad中.後期調用只需要show和dismiss即可,下面我們來看具體的show和dismiss的方法實現:
/** * 顯示loadding.默認文本為 "正在加載" */ -(void)showLoaddingView { if(isShowLoadding==YES){ return; } if(_text==nil||[_text isEqualToString:@""]){ _text=@"正在加載"; } label.text=_text; isShowLoadding=YES; angle=0; self.hidden=NO; [self addSubview:imageView]; [self addSubview:label]; [view addSubview:self]; [_parentView addSubview:view]; [YPXLoaddingView rotateImageView:imageView]; _timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(UpdateText) userInfo:nil repeats:YES]; } -(void)showLoaddingViewWithStyle:(int)style { if(style==0){//菊花加載 if(isShowLoadding==YES){ return; } if(_text==nil||[_text isEqualToString:@""]){ _text=@"正在加載"; } label.text=_text; isShowLoadding=YES; angle=0; self.hidden=NO; [self addSubview:_activityView]; [self addSubview:label]; [imageView removeFromSuperview]; [_activityView startAnimating]; [view addSubview:self]; [_parentView addSubview:view]; _timer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(UpdateText) userInfo:nil repeats:YES]; }else{//旋轉圖片加載 [self showLoaddingView]; } } /** * 顯示loadding * * @param string 顯示的文本 */ -(void)showLoaddingViewWithText:(NSString *) string { _text=string; [self showLoaddingView]; } -(void)showLoaddingViewWithText:(NSString *)text andStyle:(int)style{ _text=text; [self showLoaddingViewWithStyle:style]; }
/** * 消失loadding */ -(void)dismissLoaddingView { self.hidden=YES; isShowLoadding=NO; [_timer invalidate]; [imageView.layer removeAllAnimations]; [_activityView stopAnimating]; [view removeFromSuperview]; }
總體來說show方法中就是單純的控制了imageview和_activityView通過style來隱藏和顯示,思路很簡單,再次不做贅述.dismiss中只需要移除我們的view就好,非常簡單,同時不要忘記stop我們的_activityView以及關閉定時器就好.
致此,所有的代碼實現已經完成,我們在需要調用的地方首先實例化一次,然後使用show和dismiss即可.