思路:用UICollectionView為父容器,用於顯示個人詳細信息、多行多列Tab切換。
①抖音個人主頁包含用戶信息和用戶發布、喜歡的視頻列表,以CollectionView為父容器即可全部實現。UICollectionView包含兩個Section,第一個Section包含一個Header和一個Footer,Header 用於顯示用戶頭像、昵稱等詳細信息,Footer則用於切換“作品”與“喜歡”兩個tab,第二個section則用於顯示短視頻動圖列表。
②UICollectionView指定元素固定原理
通過重寫UICollectionViewFlowLayout中的layoutAttributesForElementsInRect方法可以讓UICollectionView顯示諸如瀑布流、水平流動布局等各種樣式的布局。其原理就是layoutAttributesForElementsInRect方法本身返回的是UICollectionView中每個元素的屬性,屬性中就包含元素的frame信息,通過修改frame值即可改變每個元素顯示的位置,這里的元素類型分為Header、Footer、Cell,判斷元素類型可將不同元素進行區分
抖音個人主頁在向上滑動時,第一個section滑動到導航欄底部時便固定位置不再上移。實現這個效果就需要將第一個section中的元素提取出來單獨處理frame值。之前也介紹了,第一個section包含一個Header和一個Footer,Header用於顯示用戶詳細信息,Footer則用於顯示切換"作品"和"喜歡"的Tab欄,因此只需判斷是否是第一個section的Header和Footer並修改frame值即可實現固定效果,具體代碼如下:
#import <UIKit/UIKit.h> @interface HoverViewFlowLayout : UICollectionViewFlowLayout @property (nonatomic, assign) CGFloat topHeight; - (instancetype)initWithTopHeight:(CGFloat)height; @end
#import "HoverViewFlowLayout.h" @implementation HoverViewFlowLayout - (instancetype)initWithTopHeight:(CGFloat)height{ self = [super init]; if (self){ self.topHeight = height; } return self; } - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSMutableArray<UICollectionViewLayoutAttributes *> *superArray = [[super layoutAttributesForElementsInRect:rect] mutableCopy]; for (UICollectionViewLayoutAttributes *attributes in [superArray mutableCopy]) { if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]) { [superArray removeObject:attributes]; } } [superArray addObject:[super layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:[NSIndexPath indexPathForItem:0 inSection:0]]]; for (UICollectionViewLayoutAttributes *attributes in superArray) { if(attributes.indexPath.section == 0) { if ([attributes.representedElementKind isEqualToString:UICollectionElementKindSectionHeader]){ CGRect rect = attributes.frame; if(self.collectionView.contentOffset.y + self.topHeight - rect.size.height > rect.origin.y) { rect.origin.y = self.collectionView.contentOffset.y + self.topHeight - rect.size.height; attributes.frame = rect; } attributes.zIndex = 5; } } } return [superArray copy]; } - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBound { return YES; } @end
③UICollectionView指定元素下拉縮放原理
UIScrollView的bounce屬性設置為YES后,UIScrollView及其子類都會有在滑動到頂部和底部時出現彈簧拉伸效果,UICollectionView、UITableView都繼承自UIScrollView,所以也有bounce屬性。實現抖音個人主頁的拉伸效果的代碼片段如下:
//UIScrollViewDelegate Delegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat offsetY = scrollView.contentOffset.y; if (offsetY < 0) { [_userInfoHeader overScrollAction:offsetY]; }else { [_userInfoHeader scrollToTopAction:offsetY]; [self updateNavigationTitle:offsetY]; } } - (void)updateNavigationTitle:(CGFloat)offsetY { if (kUserInfoHeaderHeight - HK_NAVBAR_HEIGHT*2 > offsetY) { [self setNavBarTextColor:ColorClear]; } if (kUserInfoHeaderHeight - HK_NAVBAR_HEIGHT*2 < offsetY && offsetY < kUserInfoHeaderHeight - HK_NAVBAR_HEIGHT) { CGFloat alphaRatio = 1.0f - (kUserInfoHeaderHeight - HK_NAVBAR_HEIGHT - offsetY)/[self navagationBarHeight]; [self setNavBarTextColor:[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:alphaRatio]]; } if (offsetY > kUserInfoHeaderHeight - [self navagationBarHeight]) { [self setNavBarTextColor:ColorWhite]; } } /////////////////////////////////////////////// #pragma update position when over scroll - (void)overScrollAction:(CGFloat) offsetY { CGFloat scaleRatio = fabs(offsetY)/370.0f; CGFloat overScaleHeight = (370.0f * scaleRatio)/2; _topBackground.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(scaleRatio + 1.0f, scaleRatio + 1.0f), CGAffineTransformMakeTranslation(0, -overScaleHeight)); } - (void)scrollToTopAction:(CGFloat) offsetY { CGFloat alphaRatio = offsetY/(370.0f - 44 - StatusBarHeight); _containerView.alpha = 1.0f - alphaRatio; }
效果圖:
代碼地址:
https://gitee.com/Steven_Hu/DouyinUserHomePage
參考:
https://sshiqiao.github.io/document/douyin-objectc.html#2.2