ProgressHUD原始碼詳解

SeaMoonTime發表於2018-05-15

ProgressHUD原始碼詳解

ProgressHUD是iOS開發中常用的等待控制元件(菊花控制元件),使用者網路請求等

主要結構

ProgressHUD只包含兩個程式碼檔案ProgressHUD.hProgressHUD.m,以及一個資原始檔ProgressHUD.bundle

ProgressHUD本質上為UIView的子類

@interface ProgressHUD : UIView
複製程式碼

其主要結構包含5部分:

@interface ProgressHUD()
{
	UIWindow *window;
	UIView *viewBackground;
	UIToolbar *toolbarHUD;
	UIActivityIndicatorView *spinner;
	UIImageView *imageView;
	UILabel *labelStatus;
}

複製程式碼

以上控制元件之間的結構關係如下:

image

初始化控制元件

  • show方法中呼叫
+ (void)show
{
	dispatch_async(dispatch_get_main_queue(), ^{
		[[self shared] hudCreate:nil image:nil spin:YES hide:NO interaction:YES];
	});
}
複製程式碼
  • 初始化

- (void)hudCreate:(NSString *)status image:(UIImage *)image spin:(BOOL)spin hide:(BOOL)hide interaction:(BOOL)interaction
{
    //初始化toolbarHUD
	if (toolbarHUD == nil)
	{
		toolbarHUD = [[UIToolbar alloc] initWithFrame:CGRectZero];
		toolbarHUD.translucent = YES;
		toolbarHUD.backgroundColor = self.hudColor;
		toolbarHUD.layer.cornerRadius = 10;
		toolbarHUD.layer.masksToBounds = YES;
		[self registerNotifications];
	}
	
	if (toolbarHUD.superview == nil)
	{
        //如果與背景檢視不可互動,初始化背景檢視,背景檢視為toolbarHUD的父檢視
		if (interaction == NO)
		{
			viewBackground = [[UIView alloc] initWithFrame:window.frame];
			viewBackground.backgroundColor = self.backgroundColor;
			[window addSubview:viewBackground];
			[viewBackground addSubview:toolbarHUD];
		}//如果與背景檢視可互動,頂層window為toolbarHUD的父檢視
		else [window addSubview:toolbarHUD];
	}
	//初始化spinner(菊花),並新增到toolbarHUD
	if (spinner == nil)
	{
		spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
		spinner.color = self.spinnerColor;
		spinner.hidesWhenStopped = YES;
	}
	if (spinner.superview == nil) [toolbarHUD addSubview:spinner];
	//初始化imageView並新增到toolbarHUD
	if (imageView == nil)
	{
		imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 28, 28)];
	}
	if (imageView.superview == nil) [toolbarHUD addSubview:imageView];
	//初始化labelStatus,新增到toolbarHUD
	if (labelStatus == nil)
	{
		labelStatus = [[UILabel alloc] initWithFrame:CGRectZero];
		labelStatus.font = self.statusFont;
		labelStatus.textColor = self.statusColor;
		labelStatus.backgroundColor = [UIColor clearColor];
		labelStatus.textAlignment = NSTextAlignmentCenter;
		labelStatus.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
		labelStatus.numberOfLines = 0;
	}
	if (labelStatus.superview == nil) [toolbarHUD addSubview:labelStatus];

    //文字資訊新增
	labelStatus.text = status;
	labelStatus.hidden = (status == nil) ? YES : NO;
	//圖片資訊新增
	imageView.image = image;
	imageView.hidden = (image == nil) ? YES : NO;
	//spinner(菊花)旋轉(或停止)
	if (spin) [spinner startAnimating]; else [spinner stopAnimating];
	
	[self hudSize];//尺寸設定
	[self hudPosition:nil];//位置設定
	[self hudShow];//動畫顯示
	//定時關閉
	if (hide) [self timedHide];
}

複製程式碼

設定Size


- (void)hudSize
{
	CGRect rectLabel = CGRectZero;
	CGFloat widthHUD = 100, heightHUD = 100;//toolbarHUD預設的尺寸
	if (labelStatus.text != nil)
	{
		NSDictionary *attributes = @{NSFontAttributeName:labelStatus.font};
		NSInteger options = NSStringDrawingUsesFontLeading | NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin;
		rectLabel = [labelStatus.text boundingRectWithSize:CGSizeMake(200, 300) options:options attributes:attributes context:NULL];//計算文字所需的Rect

        //toolbarHUD對應的Rect
		widthHUD = rectLabel.size.width + 50;
		heightHUD = rectLabel.size.height + 75;

		if (widthHUD < 100) widthHUD = 100;
		if (heightHUD < 100) heightHUD = 100;

		rectLabel.origin.x = (widthHUD - rectLabel.size.width) / 2;
		rectLabel.origin.y = (heightHUD - rectLabel.size.height) / 2 + 25;
	}
	
	toolbarHUD.bounds = CGRectMake(0, 0, widthHUD, heightHUD);
	//imageView對應的位置
	CGFloat imageX = widthHUD/2;
	CGFloat imageY = (labelStatus.text == nil) ? heightHUD/2 : 36;
	imageView.center = spinner.center = CGPointMake(imageX, imageY);
	
	labelStatus.frame = rectLabel;
}

複製程式碼

設定postion

- (void)hudPosition:(NSNotification *)notification
{
	CGFloat heightKeyboard = 0;
	NSTimeInterval duration = 0;
	//計算鍵盤高度
	if (notification != nil)
	{
		NSDictionary *info = [notification userInfo];
		CGRect keyboard = [[info valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
		duration = [[info valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
		if ((notification.name == UIKeyboardWillShowNotification) || (notification.name == UIKeyboardDidShowNotification))
		{
			heightKeyboard = keyboard.size.height;
		}
	}
	else heightKeyboard = [self keyboardHeight];
	//toolbarHUD的水平中心在螢幕中間,垂直中心在除去鍵盤高度的中心。
	CGRect screen = [UIScreen mainScreen].bounds;
	CGPoint center = CGPointMake(screen.size.width/2, (screen.size.height-heightKeyboard)/2);
	//動態設定toolbarHUD的中心
	[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionAllowUserInteraction animations:^{
		self->toolbarHUD.center = CGPointMake(center.x, center.y);
	} completion:nil];
	if (viewBackground != nil) viewBackground.frame = window.frame;
}

複製程式碼
  • 注意:
  1. toolbarHUD的水平中心在螢幕中間,垂直中心在除去鍵盤高度的中心。
CGPoint center = CGPointMake(screen.size.width/2, (screen.size.height-heightKeyboard)/2);
複製程式碼
  1. toolbarHUD的垂直中心會根據鍵盤訊息進行調整。

顯示

- (void)hudShow
{
	if (self.alpha == 0)
	{
		self.alpha = 1;
		toolbarHUD.alpha = 0; //初始設定為完全透明,實現隱藏功能
		toolbarHUD.transform = CGAffineTransformScale(toolbarHUD.transform, 1.4, 1.4); //放大

		UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut; //動畫模式設定
        //動畫顯示
		[UIView animateWithDuration:0.15 delay:0 options:options animations:^{
			self->toolbarHUD.transform = CGAffineTransformScale(self->toolbarHUD.transform, 1/1.4, 1/1.4); //恢復正常
			self->toolbarHUD.alpha = 1; //設為不透明,顯示出來
		} completion:nil];
	}
}
複製程式碼

隱藏

- (void)hudHide
{
	if (self.alpha == 1)
	{
		UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn; //動畫模式設定
		[UIView animateWithDuration:0.15 delay:0 options:options animations:^{
			self->toolbarHUD.transform = CGAffineTransformScale(self->toolbarHUD.transform, 0.7, 0.7); //縮小為原來的0.7倍
			self->toolbarHUD.alpha = 0; //設定為透明,實現隱藏
		}
		completion:^(BOOL finished) {
			[self hudDestroy]; //刪除各控制元件的依賴關係,再刪除控制元件
			self.alpha = 0;
		}];
	}
}

複製程式碼

相關文章