目錄
做內(nèi)容展示頁(yè)的時(shí)候,經(jīng)常會(huì)用到WKWebView+UITableView的混排功能窜锯,現(xiàn)在此做一個(gè)總結(jié),該功能的實(shí)現(xiàn)我采用了四種方法作岖。
- 1各吨、 tableView.tableHeaderView = webView 撐開(kāi)webView
- 2、[webView.scrollView addSubview:tableView] + 占位Div
- 3层宫、tableView.tableHeaderView = webView 不撐開(kāi)webView (推薦)
- 4、scrollView addSubView: webView & tableView (推薦)
- 5其监、結(jié)尾
方案1:
webView作為tableView的Header, 撐開(kāi)webView萌腿,顯示渲染全部?jī)?nèi)容,當(dāng)內(nèi)容過(guò)多時(shí)抖苦,比如大量高清圖片時(shí)毁菱,容易造成內(nèi)存暴漲(不建議使用)米死,此方案簡(jiǎn)單粗暴 , 僅適用于內(nèi)容少的場(chǎng)景,具體實(shí)現(xiàn)不在此贅述贮庞,直接看代碼峦筒。
方案2:
簡(jiǎn)書(shū)的內(nèi)容頁(yè)實(shí)現(xiàn)方案 : UIWebView與UITableView的嵌套方案
將 tableView 加到 webView.scrollView 上, webView 加載的HTML最后留一個(gè)空白占位div,用于確定 tableView 的位置窗慎,在監(jiān)聽(tīng)到webView.scrollView.contentSize變化后物喷,不斷調(diào)整tableView的位置,同時(shí)將該div的尺寸設(shè)置為tableView的尺寸捉邢。禁用tableView和webView.scrollVie的scrollEnabled = NO脯丝,通過(guò)添加pan手勢(shì),手動(dòng)調(diào)整 contentOffset商膊。tableView的最大高度為屏幕高度伏伐,當(dāng)內(nèi)容不足一屏?xí)r,高度為內(nèi)容高度晕拆。
方案3(推薦):
webView作為tableView的Header, 但不撐開(kāi)webView藐翎。禁用tableView和webView.scrollVie的scrollEnabled = NO,通過(guò)添加pan手勢(shì),手動(dòng)調(diào)整contentOffset实幕。webView的最大高度為屏幕高度吝镣,當(dāng)內(nèi)容不足一屏?xí)r,高度為內(nèi)容高度昆庇。和方案2類(lèi)似末贾,但是不需要插入占位Div。主要代碼如下:
-
步驟1:初始化配置
//禁用自帶的滑動(dòng)功能
_webView.scrollView.scrollEnabled = NO;
_tableView.scrollEnabled = NO;
// 給父視圖添加拖動(dòng)手勢(shì)
[self.view addGestureRecognizer:self.panRecognizer];
-
步驟2:手動(dòng)調(diào)整contentOffset
/// 拖拽手勢(shì)整吆,模擬UIScrollView滑動(dòng)
- (void)handlePanGestureRecognizer:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
//開(kāi)始拖動(dòng)拱撵,移除之前所有的動(dòng)力行為
[self.dynamicAnimator removeAllBehaviors];
}
break;
case UIGestureRecognizerStateChanged: {
CGPoint translation = [recognizer translationInView:self.view];
//拖動(dòng)過(guò)程中調(diào)整scrollView.contentOffset
[self scrollViewsSetContentOffsetY:translation.y];
[recognizer setTranslation:CGPointZero inView:self.view];
}
break;
case UIGestureRecognizerStateEnded: {
// 這個(gè)if是為了避免在拉到邊緣時(shí),以一個(gè)非常小的初速度松手不回彈的問(wèn)題
if (fabs([recognizer velocityInView:self.view].y) < 120) {
if ([self.tableView sl_isTop] &&
[self.webView.scrollView sl_isTop]) {
//頂部
[self performBounceForScrollView:self.webView.scrollView isAtTop:YES];
} else if ([self.tableView sl_isBottom] &&
[self.webView.scrollView sl_isBottom]) {
//底部
if (self.tableView.frame.size.height < self.view.sl_height) { //tableView不足一屏表蝙,webView bounce
[self performBounceForScrollView:self.webView.scrollView isAtTop:NO];
} else {
[self performBounceForScrollView:self.tableView isAtTop:NO];
}
}
return;
}
-
步驟3:模擬慣性和邊緣反彈效果
//動(dòng)力元素 力的操作對(duì)象
SLDynamicItem *item = [[SLDynamicItem alloc] init];
item.center = CGPointZero;
__block CGFloat lastCenterY = 0;
//慣性力
UIDynamicItemBehavior *inertialBehavior = [[UIDynamicItemBehavior alloc] initWithItems:@[item]];
//給item添加初始線(xiàn)速度 手指松開(kāi)時(shí)的速度
[inertialBehavior addLinearVelocity:CGPointMake(0, -[recognizer velocityInView:self.view].y) forItem:item];
//減速度 無(wú)速度阻尼
inertialBehavior.resistance = 2;
__weak typeof(self) weakSelf = self;
inertialBehavior.action = ^{
//慣性力 移動(dòng)的距離
[weakSelf scrollViewsSetContentOffsetY:lastCenterY - item.center.y];
lastCenterY = item.center.y;
};
//注意拴测,self.inertialBehavior 的修飾符是weak,慣性力結(jié)束停止之后府蛇,會(huì)釋放inertialBehavior對(duì)象集索,self.inertialBehavior = nil
self.inertialBehavior = inertialBehavior;
[self.dynamicAnimator addBehavior:inertialBehavior];
}
//反彈力
- (void)performBounceForScrollView:(UIScrollView *)scrollView isAtTop:(BOOL)isTop {
if (!self.bounceBehavior) {
//移除慣性力
[self.dynamicAnimator removeBehavior:self.inertialBehavior]; //吸附力操作元素
SLDynamicItem *item = [[SLDynamicItem alloc] init];
item.center = scrollView.contentOffset;
//吸附力的錨點(diǎn)Y
CGFloat attachedToAnchorY = 0;
if (scrollView == self.tableView) {
//頂部時(shí)吸附力的Y軸錨點(diǎn)是0 底部時(shí)的錨點(diǎn)是Y軸最大偏移量
attachedToAnchorY = isTop ? 0 : [self.tableView sl_maxContentOffsetY];
}else {
attachedToAnchorY = 0;
}
//吸附力
UIAttachmentBehavior *bounceBehavior = [[UIAttachmentBehavior alloc] initWithItem:item attachedToAnchor:CGPointMake(0, attachedToAnchorY)];
//吸附點(diǎn)的距離
bounceBehavior.length = 0;
//阻尼/緩沖
bounceBehavior.damping = 1;
//頻率
bounceBehavior.frequency = 2;
bounceBehavior.action = ^{
scrollView.contentOffset = CGPointMake(0, item.center.y);
};
self.bounceBehavior = bounceBehavior;
[self.dynamicAnimator addBehavior:bounceBehavior];
}
}
方案2和3依賴(lài)于 UIKit 中的動(dòng)力學(xué)/仿真物理學(xué)模塊,去實(shí)現(xiàn)松手后的慣性滑動(dòng)和邊緣反彈效果汇跨,涉及的類(lèi)主要包括 UIDynamicAnimator务荆、UIDynamicItemBehavior、UIAttachmentBehavior穷遂、UIDynamicItem蛹含,我也利用這些類(lèi)自定義繼承于UIView的類(lèi)實(shí)現(xiàn)UIScrollView的效果,詳情可以去看代碼塞颁。
方案4(推薦):
[scrollView addSubView: webView & tableView]; scrollView.contenSize = webView.contenSize + tableView.contenSize; webView和tableView的最大高度為一屏高浦箱,并禁用scrollEnabled=NO吸耿,然后根據(jù)scrollView的滑動(dòng)偏移量調(diào)整webView和tableView的展示區(qū)域contenOffset。
-
步驟1:確定webView和tableView的高度
//添加觀(guān)察者 監(jiān)聽(tīng)webView 和tableView 的contentSize
- (void)addKVO{
[self.webView addObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))
options:NSKeyValueObservingOptionNew
context:nil];
[self.webView addObserver:self forKeyPath:@"scrollView.contentSize" options:NSKeyValueObservingOptionNew context:nil];
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
}
/// 根據(jù)WebView和tableView的ContentSize變化酷窥,調(diào)整父scrollView.contentSize咽安、WebView和tableView的高度位置、展示區(qū)域
- (void)updateContainerScrollViewContentSize{
self.containerScrollView.contentSize = CGSizeMake(self.view.sl_width, _webViewContentHeight + _tableViewContentHeight);
//如果內(nèi)容不滿(mǎn)一屏蓬推,則webView妆棒、tableView高度為內(nèi)容高,超過(guò)一屏則最大高為一屏高
CGFloat webViewHeight = (_webViewContentHeight < self.view.sl_height) ? _webViewContentHeight : self.view.sl_height ;
CGFloat tableViewHeight = _tableViewContentHeight < self.view.sl_height ? _tableViewContentHeight : self.view.sl_height;
self.contentView.sl_height = webViewHeight + tableViewHeight;
self.webView.sl_height = webViewHeight <= 0.1 ?0.1 :webViewHeight;
self.tableView.sl_height = tableViewHeight;
self.tableView.sl_y = self.webView.sl_height;
//更新展示區(qū)域
[self scrollViewDidScroll:self.containerScrollView];
}
-
步驟2:根據(jù)scrollView的偏移量調(diào)整webView和tableView的的位置和偏移量
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (_containerScrollView != scrollView) {
return;
}
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat webViewHeight = self.webView.sl_height;
CGFloat tableViewHeight = self.tableView.sl_height;
if (offsetY <= 0) {
//頂部下拉
self.contentView.sl_y = 0;
self.webView.scrollView.contentOffset = CGPointZero;
self.tableView.contentOffset = CGPointZero;
}else if(offsetY < _webViewContentHeight - webViewHeight){
//父scrollView偏移量的展示范圍在webView的最大偏移量?jī)?nèi)容區(qū)域
//contentView相對(duì)位置保持不動(dòng)沸伏,調(diào)整webView的contentOffset
self.contentView.sl_y = offsetY;
self.webView.scrollView.contentOffset = CGPointMake(0, offsetY);
self.tableView.contentOffset = CGPointZero;
}else if(offsetY < _webViewContentHeight){
//webView滑到了底部
self.contentView.sl_y = _webViewContentHeight - webViewHeight;
self.webView.scrollView.contentOffset = CGPointMake(0, _webViewContentHeight - webViewHeight);
self.tableView.contentOffset = CGPointZero;
}else if(offsetY < _webViewContentHeight + _tableViewContentHeight - tableViewHeight){
//父scrollView偏移量的展示范圍到達(dá)tableView的最大偏移量?jī)?nèi)容區(qū)域
//調(diào)整tableView的contentOffset
self.contentView.sl_y = offsetY - webViewHeight;
self.tableView.contentOffset = CGPointMake(0, offsetY - _webViewContentHeight);
self.webView.scrollView.contentOffset = CGPointMake(0, _webViewContentHeight - webViewHeight);
}else if(offsetY <= _webViewContentHeight + _tableViewContentHeight ){
//tableView滑到了底部
self.contentView.sl_y = self.containerScrollView.contentSize.height - self.contentView.sl_height;
self.webView.scrollView.contentOffset = CGPointMake(0, _webViewContentHeight - webViewHeight);
self.tableView.contentOffset = CGPointMake(0, _tableViewContentHeight - tableViewHeight);
}else {
}
}
5糕珊、結(jié)尾
涉及 WKWebView的使用、WKWebView+UITableView混排毅糟、UIScrollView實(shí)現(xiàn)原理红选、WKWebView離線(xiàn)緩存功能 等更多內(nèi)容都在 https://github.com/wsl2ls/iOS_Tips
iOS_Tips集合簡(jiǎn)介:
1、暗黑模式
2姆另、AppleID登錄應(yīng)用
3喇肋、AVFoundation 高仿微信相機(jī)拍攝和編輯
4、AVFoundation 人臉檢測(cè)
5迹辐、AVFoundation 實(shí)時(shí)濾鏡
6蝶防、GPUImage框架的使用
7、VideoToolBox和AudioToolBox音視頻編解碼
8明吩、OpenGL ES學(xué)習(xí)
9间学、LeetCode算法練習(xí)
10、鍵盤(pán)和UIMenuController的并存問(wèn)題
11印荔、iOS Crash防護(hù)
12低葫、WKWebView相關(guān)
如果需要跟我交流的話(huà):
※ Github: https://github.com/wsl2ls
※ 掘金:https://juejin.im/user/5c00d97b6fb9a049fb436288
※ 簡(jiǎn)書(shū):http://www.reibang.com/u/e15d1f644bea