V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
forkon
V2EX  ›  iDev

关于 iOS 手势交互的一个问题,问了一圈居然无人能解,难道在 iOS 上实现这个手势交互是个无解的难题?

  •  
  •   forkon · 2018-04-19 08:28:19 +08:00 · 11944 次点击
    这是一个创建于 2444 天前的主题,其中的信息可能已经有所发展或是发生改变。

    [前提] scrollViewA 和 scrollViewB 在同一个父视图里面,不嵌套,且 scrollViewA 在 scrollViewB 的之上。

    [要实现的交互] 手指先是在 scrollViewA 里面向上滑动,当 scrollViewA 滚动到最底下时,scrollViewB 接着滚动,这个过程中手指从未抬起(相当于 touch 事件从 scrollViewA 传到了 scrollViewB ?)。

    +-----------------------------------+ | | | | | | | | | | ^ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | scrollViewA | | | | | + | | | | | | | +---------------------------+ | | | | | | | | scrollViewB | | | | | +-----------------------------------+

    第 1 条附言  ·  2018-04-19 10:43:33 +08:00
    感谢大家的热心回复,让我大大开了脑洞,谢谢。对于这个问题,再做一点补充吧:

    1. 位于上面的 scrollViewA 的高度会随着 scrollViewB 的滚动而改变。
    2. scrollViewA 和 scrollViewB 的还要接收其它 touch 事件,所以 userInteractionEnabled 不能为 false。
    3. 由于某些原因这里不能只用一个 scrollView。
    第 2 条附言  ·  2018-04-19 11:12:56 +08:00
    经 V 友提醒,补充一下 scrollViewA 和 scrollViewB 的 frame 的情况:

    scrollViewB 完全覆盖其 superView (宽高一样),scrollViewA 与 scrollViewB 同宽且左上角对齐,但 scrollViewA 的高度始终要比 scrollViewB 小。
    60 条回复    2018-04-20 19:00:44 +08:00
    whileFalse
        1
    whileFalse  
       2018-04-19 08:59:48 +08:00
    不懂 iOS,但曾经是个前端。
    scrollViewA 能不能捕获滑动事件?捕获之后发现自己滚到头了,就向 scrollViewB 派发滑动事件即可。
    sea516
        2
    sea516  
       2018-04-19 09:08:06 +08:00
    初学者吧
    forkon
        3
    forkon  
    OP
       2018-04-19 09:16:35 +08:00
    @sea516 莫非你知道答案,请不吝赐教。

    @whileFalse 没那么简单。
    mcluyu
        4
    mcluyu  
       2018-04-19 09:27:09 +08:00
    1.A 在上 B 在下, 向上滑怎么还会滑到 B ?我理解有问题?要不你画个图表示一下。。
    2.你手放在 A 上滑不抬起,触摸事件怎么会自己跑到 B 上面呢,区分一个触摸事件的触摸范围自然是从第一次触摸时算起,要是范围在不抬起(不改变初始触摸范围)的情况下自己变了才是有问题吧?比如不小心滑动到一个按钮上面,那是不是要算点击了该按钮呢?
    3.为什么会有这么奇怪的需求,A 滑动到底部时 B 继续,那用一个 ScrollView 不就行了吗?
    3a3Mp112
        5
    3a3Mp112  
       2018-04-19 09:29:34 +08:00
    scrollViewA 生成 delegate, 当 B 到达底部的时候, 给 A 的代理发消息,做你想要做的动作。
    elgae
        6
    elgae  
       2018-04-19 09:31:26 +08:00 via iPhone
    可以做的,有个关于手势的文档,读了就明白了
    elgae
        7
    elgae  
       2018-04-19 09:34:57 +08:00 via iPhone
    @elgae 我错了
    fadaixiaohai
        8
    fadaixiaohai  
       2018-04-19 09:35:02 +08:00
    看不懂,向上滑还会滑到屏幕最底下?什么操作?
    你最好理解一下滑动的本质是什么?了解一下怎么用 layout 来做视图的滑动。
    C90
        9
    C90  
       2018-04-19 09:37:50 +08:00
    hitTest 了解一下
    sunhr
        10
    sunhr  
       2018-04-19 09:40:21 +08:00
    你可以把两个 scrollView 的 scrollEnabled 都禁掉,然后在 superview 上加一个 UIPanGestureRecognizer,自己通过这个手势去计算设置两个 scrollView 的 contentOffset
    itenyh
        11
    itenyh  
       2018-04-19 09:43:10 +08:00
    @forkon 我试了一下,基本实现了你的需求,等下空了发上来,不过有点绕。感觉 @sea516 有更简单的办法。
    zvving
        12
    zvving  
       2018-04-19 09:44:06 +08:00
    拆分的目的是什么,想达到什么效果?为什么不用一个 scroll view 解决?
    dcty
        13
    dcty  
       2018-04-19 09:47:56 +08:00
    UITableview
    >CellOne ( firstScrollView )
    >CellTwo ( secondScrollview )

    cellHeight equalTo Scrollview ContentHeight

    这个是能满足需求的,并且从扩展性和性能上来说,也不至于有特别大的问题

    如果说你担心的是复用问题,或者懒加载问题,那么则应该想办法用 TableView 或者 collectionview 来解决
    另外多个 Scrollview 嵌套是官方不推荐的,当然我们也实现过,能实现,从技术的角度出发,实现有点戳,并且不是特别优,从用户体验的角度出发,没毛病。

    楼上的人也有提到过,你这个需求如果不是为了扩展性,一个 Scrollview 就解决了啊。
    forkon
        14
    forkon  
    OP
       2018-04-19 09:50:00 +08:00
    @mcluyu 很用心的回复,多谢……需求就是这么奇葩,而且还不能只用一个 ScrollView。
    @3a3Mp112 可以通过这种方法改变 B 的 contentOffset,但手指抬起之后 B 没有一个减速停止的过程。
    @elgae 哪个文档
    tigerZhang
        15
    tigerZhang  
       2018-04-19 09:53:17 +08:00
    同意楼上的观点。
    itenyh
        16
    itenyh  
       2018-04-19 09:55:18 +08:00
    懒得写了,简单说一下核心思想:和 @sunhr 实质是一样的。 首先 superview 是一个 scrollview (简称 C ),superview 上摆放你的 scrollviewA ( A ), scrollviewB ( B ):
    1. AB 把 C 完全遮住
    2. AB userInteractionEnabled = NO
    3. C 的 contentsize 的 height (如果你是垂直滑动)等于 AB 的 contentsize 的 height 之和。
    好了,现在你滑动手势并不会让 AB 移动,而是让 C 移动,不过你看不到。这样,你可以通过 C 的 contentoffset 的变化来操纵 AB 的 contentoffset 的变化了。
    核心思路是这样,还有些细节,慢慢搞吧。
    sunhr
        17
    sunhr  
       2018-04-19 10:02:05 +08:00
    @itenyh

    兄弟,你这个肯定是不行的~

    1、如果 C 是 scrollView 的话,AB 会跟着滚动的

    2、userInteractionEnabled = NO 的话,如果 AB 需要接受 touch 事件就杯具了呐,应该是 scrollEnabled = NO
    itenyh
        18
    itenyh  
       2018-04-19 10:05:23 +08:00
    @sunhr 第一点经我试验,AB 肯定是不会跟随的。 第二点如果 AB 需要接受 touch 事件就杯具了,这特么确实是个悲剧!看看有没有办法改进。
    snail1988
        19
    snail1988  
       2018-04-19 10:08:19 +08:00
    @forkon 两个方案都很简单 1 上面 scrollview 放在下面大 scrollview 上,自己实现 UIGestureRecognizerDelegate 允许两个 scrollview 同时相应手势,然后家逻辑判断哪个 scrollview 不能动即可
    snail1988
        20
    snail1988  
       2018-04-19 10:09:33 +08:00
    @forkon 第二个方案 上层覆盖透明 scrollview 把 offset 实时同步给下面两个 scrollview
    建议第一个 很简单 自己实现 pan 手势代理即可
    elgae
        22
    elgae  
       2018-04-19 10:11:49 +08:00
    @C90 强行 hitTest
    forkon
        23
    forkon  
    OP
       2018-04-19 10:12:11 +08:00
    @sunhr 我现在用的就是类似这种方法,但是效果不理想,手指抬起之后,scrollView 没有一个减速停止的过程。

    @zvving 设计上是在上面的 scrollView 的背景色是半透明的,当点击下面的 scrollView 上的内容时,上面的 scrollView 作相应的显示。

    @dcty 达不到设计的要求。

    @itenyh 感谢,我也小激动了一把,不过,A 和 B 的 userInteractionEnabled 都不能为 NO,还有其它 touch 事件要处理。
    amon
        24
    amon  
       2018-04-19 10:12:29 +08:00
    这样的交互简直是爆炸。
    建议弄成一个 ScrollView,或者 TableView。
    forkon
        25
    forkon  
    OP
       2018-04-19 10:22:51 +08:00
    @elgae
    @C90

    hitTest 也试过了,hitTest 只有在手指按下的瞬间起作用,按下之后在滑动的过程中系统就不会对其它 view 做 hitTest 了。
    loveuqian
        26
    loveuqian  
       2018-04-19 10:29:13 +08:00
    为啥要这么做?
    这不就是一个 table2 个 cell ?
    ichanne
        27
    ichanne  
       2018-04-19 10:38:02 +08:00
    什么垃圾需求才需要这样设计,说吧,抄的哪个 App,这样问题更好解决
    learnshare
        28
    learnshare  
       2018-04-19 10:42:41 +08:00
    我感觉这个交互不是很自然,一个页面中可滚动区域应该最多只有一个
    forkon
        29
    forkon  
    OP
       2018-04-19 10:46:16 +08:00
    @ichanne
    @learnshare

    从设计师的描述来看这样的效果还是蛮不错的,哈哈。
    ichanne
        30
    ichanne  
       2018-04-19 10:47:40 +08:00
    @forkon #29 直接问设计师抄的哪个 App
    cnbobolee
        31
    cnbobolee  
       2018-04-19 10:49:15 +08:00
    按你说的原理上是不行的,上面滑动到底,手指不离开事件不会转移到下面的 scrollView,但是实现你这个需求可以用其他方法,比如上面 scrollView 和下面 scrollView 进行通信。
    sunhr
        32
    sunhr  
       2018-04-19 10:52:30 +08:00
    @forkon 要减速效果,自己根据 pan 的速度搞一下就好啦,很简单的
    forkon
        33
    forkon  
    OP
       2018-04-19 10:53:56 +08:00
    @snail1988

    方案 1:不是很明白,scrollViewA 滑到底就滑不动了,还能让 scrollViewB 继续滑动?加 pan 手势改变 offset 的话,手指抬起没有减速停下来的效果。
    方案 2:覆盖透明 scrollView,那下来的两个 scrollView 怎么接收其它的 touch 事件?
    LeoNG
        34
    LeoNG  
       2018-04-19 11:02:12 +08:00
    你没有说两个 scrollview 的 frame。纵向平分 super view ?
    forkon
        35
    forkon  
    OP
       2018-04-19 11:05:44 +08:00
    @cnbobolee 我在想能不能在上面的滑到底之后,把当前的 touch 事件取消掉,上面的 scrollView 的 userInteractionEnabled 临时置为 false,手指继续滑动,系统生成新的 touch 事件,此时下面的 scrollView 捕获事件……经测试,手指不抬起就不会有新的 touch 事件产生……
    dcty
        36
    dcty  
       2018-04-19 11:08:53 +08:00
    难道是第一个 Scrollview 需要做视觉差的效果?
    如果说,我说的第一个方案完全满足需求,不服来辩。
    forkon
        37
    forkon  
    OP
       2018-04-19 11:14:57 +08:00
    @LeoNG 谢提醒,已补充:scrollViewB 完全覆盖其 superView (宽高一样),scrollViewA 与 scrollViewB 同宽且左上角对齐,但 scrollViewA 的高度始终要比 scrollViewB 小。

    @dcty 不是视差的效果,最多可以说是联动吧。
    itenyh
        38
    itenyh  
       2018-04-19 11:15:05 +08:00   ❤️ 1
    @forkon 继续谈谈 ABC 三个 scrollview 这样的架构。刚才的 16 楼的方案,有 userInteractionEnabled 必须设置为 NO 的致命问题,这其实是彻底放弃了 AB 对任何手势的响应权利。但是这个方案明显的优点在于利用 C 的手势,解决 AB 滑动以及其流畅切换的问题。
    那么思考一下,我能不能在 scroll 的时候让 C 来响应,而 touch 让 AB 来响应。从更底层来思考一下,当手指触碰到屏幕并产生动作后,系统应该要决定两件事:1、谁来响应(通过 hittest ) 2、如何响应( scroll,touch......)。能不能在知道是 scroll 后让 C 来响应,touch 则 redirect 给 AB ?
    kera0a
        39
    kera0a  
       2018-04-19 11:19:40 +08:00
    应该可以通过手势代理方法 shouldRecognizeSimultaneously,让两个 ScrollView 同时接受滑动手势,之后根据状态两个 ScrollView 做不同的处理(不用让手势停止,只需重载 ScrollView 获取手势事件做相应操作, 方法之后继续处理)

    难点是让两个 ScrollView 同时接受手势, 但嵌套 ScrollView 的教程这么多,你多搜搜就行,我嘚吧嘚吧这么多可不想写测试代码。
    forkon
        40
    forkon  
    OP
       2018-04-19 11:28:23 +08:00
    @itenyh 很好的思路,但是似乎并不好解决,难点是怎么 touch redirect 给其它 view。

    @kera0a 是我没找着吗,我看到的都是同步 contentOffset,显然这种做法在这里用不上。
    dreamCatcher
        41
    dreamCatcher  
       2018-04-19 12:25:06 +08:00
    问题很复杂很怪,往往是问题问错了
    肯定有其他办法解决源头问题
    cnbobolee
        42
    cnbobolee  
       2018-04-19 12:40:18 +08:00
    @forkon 最好不要这么干,首先手势不产生行为的话是没有事件触发的(这个哪个平台都是这样设计的),还有就是你说的临时取消之类的操作,这个操作理论上是可以的,实际操作后不会随时变化的,环境太复杂。
    snail1988
        43
    snail1988  
       2018-04-19 12:49:59 +08:00   ❤️ 1
    @forkon 我说的方案 1 有个人写了个 demo 你看看是不是能达到你要的效果
    https://github.com/indulgeIn/YBMultistageScrollView
    forkon
        44
    forkon  
    OP
       2018-04-19 13:10:07 +08:00
    @snail1988 虽然和我要的效果有比较大的出入,但有一定的研究价值。
    nathanw
        46
    nathanw  
       2018-04-19 13:23:36 +08:00   ❤️ 1
    我来终结此贴吧。

    https://github.com/maxep/MXParallaxHeader
    看主要看第三个 demo.

    解决方法:
    B 放在 A 里面,A 的 contentInset 的 bottom 设置大一点。
    设置 A 和 B 一起滑动,但通过 KVO 判断,使得其中一个滑动被还原,即没产生滑动效果。
    具体参考代码。
    我做了 2 个 tableView 的嵌套,用这个库改了下自己用。

    hittest 没有用,
    1.衔接的部分如果滑动未停止,手势的对象是无法突然改变。
    2.如果 scroll view C 并列放 A 和 B,这样 A 和 B 有其他点击操作会出问题。
    nathanw
        47
    nathanw  
       2018-04-19 13:35:26 +08:00
    补充楼上:
    1. 加 UIPanGestureRecognizer,滑动效果很差,和 scollview 的不一样.
    2. hittest 时还不知道是 pan 还是 tap,所以无法决定手势传给谁. a b 并列放不可行.
    nathanw
        48
    nathanw  
       2018-04-19 13:58:30 +08:00
    还有,代码里面有个 cancelsTouchesInView 最好改为 YES
    spongebobsun
        49
    spongebobsun  
       2018-04-19 16:59:46 +08:00
    这问题感觉像上个月碰到的一个面试题。。。
    expkzb
        50
    expkzb  
       2018-04-19 17:56:31 +08:00
    军儿
    forkon
        51
    forkon  
    OP
       2018-04-19 19:08:22 +08:00
    @expkzb 哪哪都能碰到你 哈哈
    forkon
        52
    forkon  
    OP
       2018-04-19 19:09:57 +08:00
    @nathanw 稍微有点遗憾 还终结不了
    w99wen
        53
    w99wen  
       2018-04-19 20:10:40 +08:00
    baseview 添加 pan,scroll1 的 offset 加 kvo,scroll 到底,scroll1 userinteraction = NO,pan 接收 gesture,控制 scroll2 的 offset。
    wezzard
        54
    wezzard  
       2018-04-20 02:16:10 +08:00
    這個很容易,多看看 WWDC
    ybh37
        55
    ybh37  
       2018-04-20 07:55:32 +08:00
    事件捕获、手势事件穿透、底层 View 动画控制
    不难啊
    不清楚这种交互为什么被描述的这么复杂?
    kitalphaj
        56
    kitalphaj  
       2018-04-20 08:07:26 +08:00
    哈哈,以后 iDev 的发帖都请参考这个标题,不然这些大佬不会出来回复的哈哈哈哈哈
    wsj195328
        57
    wsj195328  
       2018-04-20 09:02:27 +08:00
    手势代理判断当前 view
    C90
        58
    C90  
       2018-04-20 09:03:22 +08:00
    @forkon 确实 hittest 无法发挥作用
    @ybh37 大神能否出个 demo ?
    HelveticaNeue
        59
    HelveticaNeue  
       2018-04-20 13:27:37 +08:00
    @ichanne 应该是抄的 Twitter 个人页
    我做过一个差不多的。挺麻烦的。我是抄的微博。Twitter 怎么实现的看不出来
    楼主用 Reveal 看一下微博的个人页应该就明白了
    ostholz
        60
    ostholz  
       2018-04-20 19:00:44 +08:00
    个人感觉有点困难, 我们的 App 也涉及到这样的问题, 比你这个还要复杂. 至今也没有好解决办法.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3784 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 05:08 · PVG 13:08 · LAX 21:08 · JFK 00:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.