in iOS ~ read.

iOS - 右滑返回手势边缘阴影引起的思考

一、问题描述

  • 最近在工作中遇到了个优化拖拽返回阴影效果的问题,需求是第一个界面是一个隐藏了导航栏的视图,第二个界面是没有隐藏导航栏的视图,由于使用的是原生的导航栏,没有使用自定义的导航,并且导航栏的 translucent 默认设置为 false,通过打印当前的视图,对视图的情况进行分析:
/**
打印了一下控制器的view发现坐标是一样的,
于是打印了一下在视图上的列表视图,
发现列表的坐标在 trasnlucent 变化前后不一。
*/
//1 translucent = false
<UITableView: 0x7ff8e5874e00; frame = (0 0; 375 603);  
 clipsToBounds = YES; gestureRecognizers = <NSArray: 0x6080004437e0>; layer = <CALayer: 0x608000430fc0>; 
contentOffset: {0, 0}; contentSize: {375, 1055}>  
//2 translucent = true
<UITableView: 0x7fced4022c00; frame = (0 0; 375 667);  
clipsToBounds = YES; gestureRecognizers = <NSArray: 0x608000248700>; layer = <CALayer: 0x608000431000>;  
contentOffset: {0, 0}; contentSize: {375, 960}>  

通过以上数据可以看出,当我们translucent = false的时候,在控制器的视图上的视图的 0 点坐标会从导航栏左下方开始,并且在拖拽的时候,导航栏左侧没有阴影的变化,这样导航栏和控制器视图边缘在拖动返回的时候动画效果不一致,在视觉上并不是很突兀不是很美观,具体问题效果如图:

问题效果图.png

二、解决思路

  • 首先,我觉得那个阴影可能是个 UIImageView,简单在视图静态的时候去看了下层级,试图去找到那个视图,发现没有这个阴影视图。由于手里没有手机,没有查看在拖动情况下的情况,事实证明由于这个疏忽导致后面思考解决问题的方式变得复杂。
  • 之后认为,这个可能是苹果在拖动的时候为 view 添加的阴影效果,我产生了两个思路来解决这个问题:
    • 第一:在根视图控制器中控制 translucent 属性,并且使用 AOP 的方式来替换每个页面的 viewWillLayoutSubviews 方法,在这里重新为每个页面进行布局,这个方法唯一的优点是就在根控制器里面写就行不用进每个界面进行修改,可以完全满足需求,但是缺点很明显,就是要写一堆适配的冗余代码,很不友好,虽然实现了需求,但只是作为保底,接着去思考别的方案。
    • 第二:既然是基于阴影的效果,我可否在视图移动的时候为 navigationbar 去添加那个阴影效果?于是我尝试了使用 AOP 去切_UINavigationInteractiveTransition 中的 handleNavigationTransition 方法,UIPercentDrivenInteractiveTransition 中的 updateInteractiveTransition:finishInteractiveTransitioncancelInteractiveTransition 方法,具体思路是在拖拽之前创建阴影,并在移动的时候来改变阴影的颜色来达到和系统阴影相同的情况,这样就可以不修改约束代码而直接可以满足需求,但是这样做的问题就是画的不太好看,尝试了使用 shadow渐变层 两种方式,都无法满足和系统做到一致的需求。
    • 第三:在同事的提醒下,找到了问题的点,这个阴影就是个 UIImageView 只不过在静态页面中它不会出现,只当开始移动的时候才会出现,如对比图: 静态.png 拖拽时.png 目标视图.png 于是顺着层级结构图去找了下_UIParallaxDimmingView 中的 UIImageView ,查看一下它:
<UIImageView: 0x107d5c6e0; frame = (-9 0; 9 672); alpha = 0; opaque = NO; userInteractionEnabled = NO; animations = { opacity=<CABasicAnimation: 0x17442c5a0>; }; layer = <CALayer: 0x174432d80>>  

证明系统是给这个视图增加了个 opacity 动画达到的阴影效果,最终方案通过在移动的时候修改一下这个视图的 frame 完成了需求。这个暂时作为最优解。

三、思考

  • 后来仔细对比了一下页面拖拽返回前后的静态视图层级图片和拖拽时候的层级图片,发现苹果的处理简单逻辑大概是这样,当我们进行拖拽返回的时候,苹果会在UIViewControllerWrapperView中创建三个视图,第一个装载即将返回的控制器的视图,第二个视图是_UIParallaxDimmingView 猜测应该是为了做动画而生成的,最后的视图是创建在屏幕右侧的 ,在这个视图上创建一个 _UIParallaxDimmingView来装载返回前的视图,并创建了一个用来做阴影动画的 UIImageView,而且设置了 animation 动画。
  • 后来对方法二中的想法进行了反思,因为我在移动的过程中尝试去拿视图的一些阴影属性值,当时也怀疑了阴影论断的正确性,但是当时没有及时停止尝试。
comments powered by Disqus