【iOS】 多層嵌套UIScrollView,最實用的解決方式

已經(jīng)是2020年了,多層UIScrollView嵌套,也已是iOS中老生常談的問題。

Apple官方不建議將ScrollView嵌套使用,可是產(chǎn)品和設(shè)計師們就是戒不掉這個癮,沒辦法,既然是打工,拿人錢財替人解憂。

網(wǎng)上有眾多實現(xiàn)此功能的方案,但是魚龍混雜,而且大部分也不完善,完善的代碼又特別復(fù)雜,整合工作量較大,這兩天正好有時間和機(jī)會,好好研究和思考了一下最簡單的解決方案。

先上個效果圖


2020-05-28 20_48_17.gif

解決的代碼基本只在ScrollViewDidScroll里做了邏輯處理,對于已經(jīng)做好了XIB的小伙伴很友好

大概思路就是在內(nèi)層的UIScrollView(UITableView也算是UIScrollView)的ScrollViewDidScroll代理方法里面做邏輯判斷,看是否允許自己滾動。

首先我的層級結(jié)構(gòu)如下圖(藍(lán)色的內(nèi)ScrollView,會橫向平鋪多個)


78DCD371-B734-4788-B46A-3030765CF544.png

通常這種情況,在XIB中畫好了外部框之后,內(nèi)部的這個UITableVIew(UIScrollView)是由根據(jù)服務(wù)器返回的多少個分類,由代碼編寫后橫向平鋪的,這也就方便了我們使用自定義類。所以,首先我們還是逃不掉重寫一個UITableView,像這樣

QCTableView.h
@interface QCTableView : UITableView

///記錄一下坐標(biāo),為了等下滑動的時候判斷向上還是向下
@property (nonatomic, assign) CGFloat tableY;

@end

QCTableView.m
@implementation QCTableView

// 返回YES表示可以繼續(xù)傳遞觸摸事件,這樣便可實現(xiàn)了兩個嵌套的scrollView同時滾動。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

@end

沒錯,就重寫一個方法+一個屬性。
重寫這個是免不了的,我已經(jīng)試過很多方法,最后都只有重寫這個shouldRecognizeSimultaneouslyWithGestureRecognizer才能完美地將ScrollView接到的滑動手勢傳給下一層

然后就是當(dāng)前持有主ScrollView和內(nèi)ScrollView(或TableView)的Controller的ScrollViewDidScroll代理方法了

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if([scrollView isKindOfClass:[UITableView class]]){
//內(nèi)部ScrollView
        NSLog(@"滾動UITableView");
        QCTableView *qctv = (QCTableView*)scrollView;
//判斷向上滑動還是向下滑動
        if (scrollView.contentOffset.y - qctv.tableY > 0) {
            //向上
//_main_scrollView就是我的外層主ScrollView,當(dāng)它小于116或者大于0的時候,這時候還在向上滑動就說明在移動它,這時候禁止內(nèi)部ScrollView滾動,一直設(shè)置內(nèi)部ScrollView的Y軸為最后一次記錄的Y軸,以禁止它的滾動。
            if (_main_scrollView.contentOffset.y<116&&scrollView.contentOffset.y>0) {
                [scrollView setContentOffset:CGPointMake(0, qctv.tableY)];
            }
/*mainScrollContentY是我在全局聲明的變量
{
    CGFloat mainScrollContentY;
}
有條件的可以將它寫在主ScrollView的重寫對象里,我這里沒有為主Scroll重寫對象,就放在當(dāng)前Controller里了為了簡單
*/
//只在主ScrollView滾動了的條件下記錄它的坐標(biāo)。
            mainScrollContentY = _main_scrollView.contentOffset.y;
        }else if(scrollView.contentOffset.y - qctv.tableY < 0){
            //向下
//內(nèi)部ScrollView的Y大于0說明內(nèi)部ScrollView沒到頂,這時候向下移動也應(yīng)該將_main_scrollView固定在最上方,一直設(shè)置外部_main_scrollView的Y軸為最后一次記錄的Y軸,以禁止它的滾動。
            if (scrollView.contentOffset.y>=0) {
                [_main_scrollView setContentOffset:CGPointMake(0, mainScrollContentY)];
            }else if (_main_scrollView.contentOffset.y>0) {
//如果內(nèi)部Scroll的ContentOffset的Y小于0,說明內(nèi)部ScrollView到頂了,外部_main_scrollView的ContentOffset的Y又大于0,這時候向下移動應(yīng)該將外部_main_scrollView拉下來,一直設(shè)置內(nèi)部ScrollView的Y軸為0,以禁止它的滾動。
                [scrollView setContentOffset:CGPointMake(0, 0)];
            }
//只在主ScrollView滾動了的條件下記錄它的坐標(biāo)。
            mainScrollContentY = _main_scrollView.contentOffset.y;
        }
        qctv.tableY = scrollView.contentOffset.y;
    }else if(scrollView==_main_scrollView){
//外部主ScrollView
//這里主要是設(shè)置邊界,也就是頭部的高度,不允許_main_scrollView的contentOffset的Y軸大于116或小于0。
        NSLog(@"滾動MainScroll");
        if (scrollView.contentOffset.y>116) {
            [scrollView setContentOffset:CGPointMake(0, 116)];
        }else if(scrollView.contentOffset.y<0){
            [scrollView setContentOffset:CGPointMake(0, 0)];
        }
    }
}

注釋寫的很詳細(xì)了,以上就是所有的代碼了,其實UITableView中的tableY和mainScrollContentY是在實現(xiàn)了基本功能的基礎(chǔ)上加的,為的是修復(fù)一些坐標(biāo)上的BUG,理論上不應(yīng)該對任何一個ScrollView通過一直設(shè)置為固定的Y軸值(116或0)來禁止它的滾動,而是應(yīng)該通過一直設(shè)置它為最后一次允許滾動時記錄的Y軸,來避免跳動現(xiàn)象。但是為了代碼簡單明了,就先不做過多處理了。
Demo在這里,已上傳到GitHub
https://github.com/mmMrz/YXSccrollView

做這篇文章時,主要是因為我已經(jīng)用XIB寫好了這個“主Scroll”套“橫向Scroll”又套“多層UITableView”的XIB了,界面畫好了,不想再用github上完善的的實現(xiàn)庫去重新開發(fā)了,這樣的方法簡單明了,易用,移植性高。目前是我能想到的最簡單又相對完善的實現(xiàn)方法了。有更好的方法或者更簡單套用的三方庫,歡迎評論探討。

QQ群:653317062 (失蹤的新華社)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容