iOS仿百度地圖滑動(dòng)沖突解決

放輕松,分析一波

先來放一波效果圖嫌套。逆屡。。踱讨。魏蔗。


放個(gè)小版的GIF,大概看一下

在百度地圖搜索POI痹筛,展示POI列表時(shí)莺治,會(huì)有這種效果廓鞠,當(dāng)滑到底部時(shí),地圖會(huì)聯(lián)動(dòng)縮放谣旁,這里先針對列表TableView做一下分析床佳,所以沒有添加這個(gè)效果,但是是小問題榄审,先不用管砌们。

想要做成這種效果,肯定是需要兩層滾動(dòng)scrollView來實(shí)現(xiàn)搁进。底層scrollView來實(shí)現(xiàn)分段效果浪感,內(nèi)層scrollView來實(shí)現(xiàn)列表展示的作用。
這里兩個(gè)豎直滾動(dòng)的視圖饼问,就會(huì)發(fā)生滑動(dòng)沖突影兽。

這里有一些關(guān)鍵點(diǎn):

  • 底層的滑動(dòng)View,如何做成上中下三段效果匆瓜,不能用isPagingEnabled屬性來實(shí)現(xiàn)赢笨,這里用了scrollViewWillEndDragging代理加動(dòng)畫來實(shí)現(xiàn)。
  • 當(dāng)?shù)讓觭crollView滾動(dòng)至最底部時(shí)驮吱,要求不遮擋地圖的觸摸事件茧妒,可以正常的與地圖進(jìn)行交互。這里用到了響應(yīng)鏈hitTest:withEvent方法來解決左冬。
  • 雙層滾動(dòng)視圖的滑動(dòng)沖突桐筏。

為了方便,scrollView的分段三個(gè)級別用拇砰,1級梅忌,2級,3級來說明除破。

控件層級介紹:

  • 黑色層:當(dāng)為2級時(shí)牧氮,可以點(diǎn)擊button,跳轉(zhuǎn)到3級瑰枫。
  • 紅色層:scrollView踱葛,提供分段滑動(dòng)效果。
  • 白色層:展示數(shù)據(jù)列表光坝。

分析具體實(shí)現(xiàn)

1尸诽、上中下三段效果實(shí)現(xiàn)

分頁效果首先想到就是isPagingEnabled屬性,但是他有局限性盯另,不能隨便分頁性含,只能在每個(gè)分頁大小一樣的情況下使用,所以這里不能使用鸳惯。
這里主要用到了scrollView的代理方法商蕴。

//將要結(jié)束手勢拖拽叠萍,開始減速
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)

這個(gè)代理是手指離開屏幕開始減速,并且提供了兩個(gè)參數(shù):

  • velocity:手指離開屏幕時(shí)的初速度究恤。若不為0俭令,則會(huì)減速到targetContentOffset表示的目的地。
  • targetContentOffset:指針類型部宿,表示要到達(dá)的目的地。因?yàn)槭侵羔橆愋推芭龋晕覀兛梢孕薷乃闹道碚牛@就是我們可以實(shí)現(xiàn)分段效果的重點(diǎn)。

直接上代碼绵患,看一下這里面的計(jì)算邏輯雾叭。

//這里處理滑動(dòng)手勢結(jié)束后,頁面跳轉(zhuǎn)效果
    func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
        //不是底層scrollView落蝙,則退出织狐!
        if scrollView == self.tableView {
            return
        }
        let currentOffsetY = scrollView.contentOffset.y
        var gotoPointY: CGFloat = 0
        
        //計(jì)算滑動(dòng)手勢結(jié)束后,頁面準(zhǔn)備的位置 ---(這里只分析了速度velocity不為0時(shí)筏勒,當(dāng)速度為0時(shí)移迫,用下面的方式計(jì)算位置)
        if currentOffsetY <= contentOffsetMaxY && currentOffsetY > contentOffsetMidY {
            
            if velocity.y > 0 {
                gotoPointY = contentOffsetMaxY
            }else if velocity.y < 0{
                gotoPointY = contentOffsetMidY
            }
        }else if currentOffsetY < contentOffsetMidY && currentOffsetY > contentOffsetMinY {
            
            if velocity.y > 0 {
                gotoPointY = contentOffsetMidY
            }else{
                gotoPointY = contentOffsetMinY
            }
        }else{
            gotoPointY = contentOffsetMinY
        }
        //當(dāng)滑動(dòng)速度為0時(shí),判斷當(dāng)前位置距離哪一級別最近
        if velocity.y == 0 {
            
            var distance: CGFloat = contentOffsetMaxY
            let currentOffsetArray = [contentOffsetMinY, contentOffsetMidY, contentOffsetMaxY]
            for item in currentOffsetArray {
                
                let temp = abs(item - currentOffsetY)
                if distance > temp {
                    
                    distance = temp
                    gotoPointY = item
                }
            }
        }
        //動(dòng)畫到指定位置
        moveScrollView(scrollView: scrollView, initialSpringVelocity: velocity.y, movePointY: gotoPointY, isAnimate: true)
        //這里是重點(diǎn)管行,把計(jì)算好的最后位置給到targetContentOffset
        targetContentOffset.pointee = CGPoint(x: 0, y: gotoPointY)
    }

計(jì)算好需要的位置坐標(biāo)厨埋,設(shè)置到targetContentOffset屬性。moveScrollView添加動(dòng)畫

//動(dòng)畫將scrollView移動(dòng)到指定位置
    func moveScrollView(scrollView: UIScrollView, initialSpringVelocity velocity: CGFloat, movePointY: CGFloat, isAnimate: Bool) {
        //可以先設(shè)置一下當(dāng)前偏移量捐顷。
        scrollView.setContentOffset(scrollView.contentOffset, animated: false)
        UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: velocity, options: UIView.AnimationOptions.curveEaseOut, animations: {
            //動(dòng)畫到計(jì)算好的指定偏移量
            scrollView.contentOffset.y = movePointY
        }, completion: nil)
    }

這樣其實(shí)就大體實(shí)現(xiàn)了荡陷,分三段的效果。

2迅涮、視圖顯示到3級時(shí)废赞,如何實(shí)現(xiàn)不遮擋地圖觸摸事件

因?yàn)閟crollView是全屏布局,才可以全屏滾動(dòng)叮姑。這樣不可避免的就會(huì)遮擋住地圖的觸摸事件交互唉地,這跟我們的需求是不符的。所以需要我們來做一個(gè)處理戏溺,這里用到了hitTest:withEvent方法來解決渣蜗。

  • 簡單介紹一下hitTest:withEvent方法:
    響應(yīng)鏈傳遞的關(guān)鍵方法,當(dāng)手勢交互時(shí)旷祸,會(huì)逐級的用此方法來尋找到要響應(yīng)事件的視圖耕拷。我們可以重寫方法,來進(jìn)行業(yè)務(wù)需求的操作托享。

所以我們可以通過此方法骚烧,來判斷scrollView在什么時(shí)候可以響應(yīng)手勢事件浸赫,什么時(shí)候忽略手勢事件。

創(chuàng)建一個(gè)UIScrollView類赃绊,重寫父類hitTest:withEvent方法既峡,上代碼:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let hitView = super.hitTest(point, with: event)
        if hitView == self {
            return nil
        }
        return hitView
    }

解讀一下:

  • 從父類方法中,獲取到將要響應(yīng)事件的視圖hitView碧查,如果這個(gè)視圖是scrollView本身运敢,則返回nil代表這層view不響應(yīng)事件,事件會(huì)繼續(xù)傳遞忠售,就會(huì)傳遞到scrollView下一層視圖传惠。
  • 如果hitView不是scrollView本身,則表示是scrollView的子視圖響應(yīng)手勢事件稻扬,也就是說手指是觸碰的子View卦方,不是直接觸碰scrollView,所以正常return這個(gè)子視圖就好了泰佳。

這里的業(yè)務(wù)需求就是手指觸碰白色tableView才可以拖動(dòng)盼砍,觸碰紅色區(qū)域不會(huì)影響下一層的Map地圖交互。(這里因?yàn)槲抑虚g添加了一個(gè)黑色圖層逝她,是想實(shí)現(xiàn)在2級時(shí)浇坐,點(diǎn)擊黑色按鈕,可以到達(dá)3級汽绢,當(dāng)?shù)?級時(shí)吗跋,黑色按鈕設(shè)置isUserInteractionEnabled = false,不會(huì)影響地圖交互)

3宁昭、解決滑動(dòng)沖突

滑動(dòng)tableView的時(shí)候跌宛,還需要讓scrollView也同時(shí)滾動(dòng),所以首先要讓他們之間的滾動(dòng)互相不受影響积仗。
這里需要用到UIGestureRecognizerDelegate代理里的方法疆拘。

  • 還是需要自定義UITableView類,實(shí)現(xiàn)代理UIGestureRecognizerDelegate的方法??
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }

返回true時(shí)寂曹,代表tableView有多個(gè)手勢時(shí)不會(huì)干擾哎迄,這樣就不會(huì)影響到scrollView的滾動(dòng)憔涉。
有很多地方都會(huì)用到孵滞,比如給tableView添加手勢時(shí),返回true就可以多個(gè)手勢一起觸發(fā)弦牡。

  • 不互相干擾了渺氧,但是還是不能實(shí)現(xiàn)效果旨涝,還需要去控制他們什么時(shí)候才可以讓他們滾動(dòng)。
    監(jiān)聽滑動(dòng)偏移量侣背,來進(jìn)行判斷控制白华。所以用到scrollView的代理方法:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //這里主要解決滑動(dòng)沖突
        if scrollView == self.scrollView {
            //這里用 > 就好
            if scrollView.contentOffset.y > contentOffsetMaxY {
                scrollView.contentOffset.y = contentOffsetMaxY
                //設(shè)置標(biāo)記慨默,用來判斷視圖是否可以滾動(dòng)
                scrollViewMove = false
                tableViewMove = true
            }
            //當(dāng)scrollView不能滾動(dòng)時(shí),設(shè)置其偏移量
            if !scrollViewMove {
                scrollView.contentOffset.y = contentOffsetMaxY
            }
        }else if scrollView == self.tableView {
            //同scrollView的滾動(dòng)View類似操作弧腥。
            if scrollView.contentOffset.y <= 0 {
                scrollView.contentOffset.y = 0
                tableViewMove = false
                scrollViewMove = true
            }
            if !tableViewMove {
                scrollView.contentOffset.y = 0
            }
        }
    }

添加兩個(gè)標(biāo)記(其實(shí)用一個(gè)標(biāo)記也可以)厦取,來控制視圖是否可以滾動(dòng)。
這樣來控制管搪,就可以解決問題了虾攻。


重要的關(guān)鍵點(diǎn),現(xiàn)在都解決了更鲁,其他一些細(xì)枝末節(jié)就不記錄了台谢。
貼上源碼以供參考:
項(xiàng)目源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市岁经,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛇券,老刑警劉巖缀壤,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纠亚,居然都是意外死亡塘慕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門蒂胞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來图呢,“玉大人骗随,你說我怎么就攤上這事蛤织。” “怎么了鸿染?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵指蚜,是天一觀的道長。 經(jīng)常有香客問我涨椒,道長摊鸡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任蚕冬,我火速辦了婚禮免猾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘囤热。我一直安慰自己猎提,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布赢乓。 她就那樣靜靜地躺著忧侧,像睡著了一般石窑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚓炬,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天松逊,我揣著相機(jī)與錄音,去河邊找鬼肯夏。 笑死经宏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驯击。 我是一名探鬼主播烁兰,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼徊都!你這毒婦竟也來了沪斟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤暇矫,失蹤者是張志新(化名)和其女友劉穎主之,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體李根,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡槽奕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了房轿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粤攒。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖囱持,靈堂內(nèi)的尸體忽然破棺而出夯接,到底是詐尸還是另有隱情,我是刑警寧澤洪唐,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布钻蹬,位于F島的核電站,受9級特大地震影響凭需,放射性物質(zhì)發(fā)生泄漏问欠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一粒蜈、第九天 我趴在偏房一處隱蔽的房頂上張望顺献。 院中可真熱鬧,春花似錦枯怖、人聲如沸注整。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肿轨。三九已至寿冕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椒袍,已是汗流浹背驼唱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驹暑,地道東北人玫恳。 一個(gè)月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像优俘,于是被迫代替她去往敵國和親京办。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345