Ixx*_*Ixx 8 math transform matrix uiview ios
给定以下视图层次结构
root (e.g. view of a view controller)
|_superview: A view where we will draw a cross using core graphics
|_container: Clips subview
|_subview: A view where we will show a cross adding subviews, which has to align perfectly with the cross drawn in superview
|_horizontal line of cross
|_vertical line of cross
Run Code Online (Sandbox Code Playgroud)
任务:
在全局变换的情况下,必须始终对齐superview并且subview必须始终对齐."要求"部分中的更多详细信息.
语境:
上面的视图层次结构属于图表.为了提供最大的灵活性,它允许以3种不同的方式呈现图表点和相关内容:
在图表的基本视图(superview)draw方法中绘图.
添加子视图到subview.subview在zoom/pan上进行转换,并自动显示其子视图.
将子视图添加到兄弟姐妹中subview.为简单起见,未在视图层次结构中显示,因为它与问题无关.这里只提一下概述.这个方法和2.之间的区别在于,这里视图没有被转换,所以它留给内容的实现来"手动"更新所有子节点的转换.
最大的灵活性!但实现这一目标的成本实施起来有点棘手.具体点2.
目前我通过基本上处理superview核心图形绘制的变换并且subview单独地进行缩放/平移工作,但这导致冗余和容易出错,例如重复的边界检查代码等.
所以现在我正在尝试重构它以使用一个全局矩阵来存储所有变换并从中导出所有变换.将全局矩阵应用于superview绘制所使用的坐标是微不足道的,但是根据subview下一节中列出的要求推导出矩阵,而不是那么多.
我在视图层次结构部分提到"十字架",因为这是我在游乐场中使用的一个图表点的简化表示(使用x/y指南)(您可以向下滚动图像和图表).
要求:
subview的子视图,即无法触摸十字线视图(例如,将变换应用于它们) - 所有可以修改的都是subview变换.matrix.matrix然后用于计算superview(平凡)绘制的交叉坐标,以及subview(非平凡的 - 这个问题的原因)的转换矩阵.
subview从全局矩阵中导出唯一矩阵,因此允许将其他数据存储在变量中,然后将这些变量与全局矩阵一起用于计算subview矩阵.container缩放/平移期间可以更改大小/原点.这样做的原因是y轴的标签可以具有不同的长度,并且需要图表来动态地使内容大小适应标签占据的空间(在缩放和平移期间).container变化的大小时,域 - 屏幕坐标的比例必须相应地改变,使得完整的原始可见域继续被包含在中container.例如,如果我在宽度为500pt的容器框架中显示带有域[0,10]的x轴,即将域点转换为屏幕坐标的比率为500/10=50,则将容器宽度缩小到250,现在我的[0,10]域必须适合这个新宽度,其比率为25.我做了什么:
以下是我试图更好地理解问题的分步操场:
第1步(工作):
如开头所述构建层次结构,只显示在(编程)缩放和平移期间必须保持对齐的十字架.符合要求1,2,3,4和5:
特殊性:
container视图,保持简单.subview是直接的子视图superview.subview与当前superview缩放前的尺寸相同,也是为了保持简单.subview原点(0,0),这似乎是与全局矩阵同步的必要条件.subviewAnchorTranslation.这属于我在要求5下的子弹中考虑的额外数据.好的,正如你看到的一切都在这里工作.是时候尝试下一步了.
第2步(工作):
步骤1游乐场的副本和修改:
container视图,现在类似于开头描述的视图层次结构.subview现在是container继续显示在同一位置的子视图,它必须被移动到顶部和左侧-container.origin.十字架继续保持同步.满足要求:全部来自步骤1 +要求6.
要点与操场
第3步(不起作用):
到目前为止,我一直在使用从0开始的屏幕范围(可见游乐场结果的左侧).这意味着它container不能满足它包含范围的功能,即要求7.为了满足这一要求,container必须将原点包含在比率计算中.
现在还subview必须进行缩放,以便container在正确的位置放入/显示十字架.其中添加了第二个变量(第一个是subviewAnchorTranslation),我调用它contentScalingFactor,包含这个缩放,必须包含在subview矩阵计算中.
在这里,我做了多次实验,但都失败了.在当前状态下,subview以相同的帧开始,container并且当帧发生container变化时,其帧被调整+缩放.此外,subview现在在容器内部,即它的起源是现在container的起源而不是superview起源,我必须设置更新它的锚点,使原点不在(0,0)但是(-x,-y), x和y的原点坐标container,这样就可以subview继续相对于superview原点的位置.每次container改变它的原点时更新这个锚似乎是合乎逻辑的,因为这会改变从content原点到原点的相对位置superview.
我为此上传了代码 - 在这种情况下是一个完整的iOS项目,而不仅仅是一个游乐场(我最初认为它正在工作,并希望使用实际手势进行测试).在实际项目中,我正在努力改造工作,但我找不到差异.无论如何它不能很好地工作,在某些时候总是存在小的偏移并且点/交叉不同步.
好的,我如何解决这个问题,以满足所有条件.十字架必须保持同步,连续缩放/平移并改变container其间的帧.
当前的答案允许任意转换子层次结构中的任何视图。它不跟踪变换,仅转换变换点,从而回答以下问题:
\n位于子视图中的点在另一个视图的坐标系中的坐标是多少,无论该子视图已经变换了多少。
\n为了将父级与剪切容器分离并提供通用答案,我建议将它们在概念上放置在同一级别,并在视觉上以不同的顺序 (\xe2\x80\xa0):
\n\n要应用从Child到Parent的滚动、缩放或任何其他转换,请通过公共超级视图(在本示例中名为Coordinator )。
\n该方法与Stack Overflow 的答案非常相似,其中两个UIScrollView以不同的速度滚动。
请注意红色发际线和黑色发际线如何重叠,无论层次结构中任何和所有视图(包括 的视图)的位置、滚动和变换Child如何Container。
坐标转换
\n为了清楚起见,进行了简化,在子视图的坐标系中使用任意点(50,50)(该点被有效绘制),并将其转换到父视图系统中,如下所示:
\nfunc coordinate() {\n let transfer = theChild.convert(CGPoint(x:50, y:50), to: coordinator)\n let final = coordinator.convert(transfer, to: theParent)\n theParent.transformed = final\n theParent.setNeedsDisplay()\n}\nRun Code Online (Sandbox Code Playgroud)\n缩放和平移容器
\nfunc zoom(center: CGPoint, delta: CGPoint) {\n theContainer.transform = theContainer.transform.scaledBy(x: delta.x, y: delta.y)\n coordinate()\n}\n\nfunc translate(delta: CGPoint) {\n theContainer.transform = theContainer.transform.translatedBy(x: delta.x, y: delta.y)\n coordinate()\n}\nRun Code Online (Sandbox Code Playgroud)\n(\xe2\x80\xa0) 我已将Superview和Subview分别重命名为Parent和Child。
\n