思路:用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