Swift90Days – 動態變化的 UITableView 頭部
今天公司的有一個需求是實現動態的 UITableView 頭部,基本需求是:
- 預設狀態顯示一個 headerView 展示頭像。
- 上推時 headerView 縮小高度,影像逐漸模糊。
- 下拉時 headerView 的影像相應下移,方便展示全景。
下面先看看開源專案 ParallaxTableViewHeader 的實現方式,和自己的做個對比。
Blur
它的虛化效果是通過 UIImage+ImageEffects 這個 category 實現的。
具體內容就不深究了,對於影像處理這塊的類庫不太熟悉。
在 headerView 中可以這樣使用獲取一張虛化的圖片:
- (void)refreshBlurViewForNewImage
{
UIImage *screenShot = [self screenShotOfView:self];
screenShot = [screenShot applyBlurWithRadius:5 tintColor:[UIColor colorWithWhite:0.6 alpha:0.2] saturationDeltaFactor:1.0 maskImage:nil];
self.bluredImageView.image = screenShot;
}
Header
原始碼中提供了兩種工廠方法進行初始化:
+ (id)parallaxHeaderViewWithImage:(UIImage *)image forSize:(CGSize)headerSize;
+ (id)parallaxHeaderViewWithSubView:(UIView *)subView;
第一種方法是通過 image 進行初始化,會呼叫預設的 init 方法,第二種是自定義 subView 的方法。
我們只用看下預設的 init 方法:
- (void)initialSetupForDefaultHeader
{
// 初始化一個 scrollView 作為容器
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
self.imageScrollView = scrollView;
// 初始化預設大小的圖片,用於顯示
UIImageView *imageView = [[UIImageView alloc] initWithFrame:scrollView.bounds];
// 設定其拉伸模式為:上下左右間距不變,拉伸高度和寬度。
imageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
// 設定圖片填充模式:儘量填充,適當裁剪
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.image = self.headerImage;
self.imageView = imageView;
[self.imageScrollView addSubview:imageView];
// 設定顯示的標籤文字
CGRect labelRect = self.imageScrollView.bounds;
labelRect.origin.x = labelRect.origin.y = kLabelPaddingDist;
labelRect.size.width = labelRect.size.width - 2 * kLabelPaddingDist;
labelRect.size.height = labelRect.size.height - 2 * kLabelPaddingDist;
UILabel *headerLabel = [[UILabel alloc] initWithFrame:labelRect];
headerLabel.textAlignment = NSTextAlignmentCenter;
headerLabel.numberOfLines = 0;
headerLabel.lineBreakMode = NSLineBreakByWordWrapping;
headerLabel.autoresizingMask = imageView.autoresizingMask;
headerLabel.textColor = [UIColor whiteColor];
headerLabel.font = [UIFont fontWithName:@"AvenirNextCondensed-Regular" size:23];
self.headerTitleLabel = headerLabel;
[self.imageScrollView addSubview:self.headerTitleLabel];
// 設定虛化的圖片,預設 alpha 為0,即完全透明
self.bluredImageView = [[UIImageView alloc] initWithFrame:self.imageView.frame];
self.bluredImageView.autoresizingMask = self.imageView.autoresizingMask;
self.bluredImageView.alpha = 0.0f;
[self.imageScrollView addSubview:self.bluredImageView];
[self addSubview:self.imageScrollView];
}
大概瞭解了整個 view 的結構,不太清楚為什麼要通過截圖的方式獲取圖片。
通過實現 UISCrollViewDelegate
中的 scrollViewDidScroll
方法來監聽 UITableView 的滑動事件。
如果當前 UITableView 滑動了,則會呼叫 headerView 的 layoutHeaderViewForScrollViewOffset
方法:
- (void)layoutHeaderViewForScrollViewOffset:(CGPoint)offset
{
CGRect frame = self.imageScrollView.frame;
// 如果是上推
if (offset.y > 0)
{
frame.origin.y = offset.y *kParallaxDeltaFactor;
self.imageScrollView.frame = frame;
// 設定虛化圖層的 alpha 值。乘2是為了增大虛化梯度
self.bluredImageView.alpha = 2 * (offset.y / kDefaultHeaderFrame.size.height) ;
// 裁切 subview
self.clipsToBounds = YES;
}
// 如果是下拉
else
{
CGFloat delta = 0.0f;
CGRect rect = kDefaultHeaderFrame;
delta = fabs(offset.y);
// 為了保持 header 的 top 對齊需要設定 y 座標
rect.origin.y -= delta;
rect.size.height += delta;
self.imageScrollView.frame = rect;
self.clipsToBounds = NO;
// 設定 label 的 alpha 值
self.headerTitleLabel.alpha = 1 - delta / kMaxTitleAlphaOffset;
}
}
上推的時候主要工作是虛化圖片,下拉的時候主要工作是設定圖片高度和座標。這個和我自己寫的基本思路相同。
大概就是這樣,引用中放了另一個類似的專案供大家參考。這個專案與 Swift 無關,不過實現的思路可以借鑑。