Given the following hierarchy of views:
root (eg 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
Task:
superview and subview crosses should always be aligned with global conversion. See details in the Requirements section.
Context:
The hierarchy presented above refers to a chart. To provide maximum flexibility, it allows you to represent graph points and related content in three different ways:
Drawing in the base view of the chart ( superview ) draw .
Adding subheadings to subview . subview converted when zooming / panning, and at the same time automatically its subview .
Adding subviews to sibling subview . Not presented in the hierarchy view for simplicity, and because it is not related to the problem. Just a mention here to give an overview. The difference between this method and 2. is that the view is not converted here, so he left to implement the content to manually update the conversion of all child elements.
Maximum flexibility! But with this comes a cost that is a little difficult to realize. In particular, point 2.
Currently, I have gotten zooming / panning, mainly handling transformations for graphical drawing of superview and subview separately, but this leads to redundancy and error rate, for example. repeated code for border checks, etc.
So, now I'm trying to reorganize it to use one global matrix to store all the transformations and get everything from it. Applying the global matrix to the coordinates used by superview for drawing is trivial, but there are not many that take the matrix out of subview , given the requirements listed in the next section.
I mention โcrossesโ in the presentation hierarchy section because this is what I use on my playgrounds as a simplified representation of a single point in the graph (with x / y recommendations) (you can scroll down for images and gists).
Requirements:
- Content can be scaled and panned.
- Crosses are always perfectly aligned.
subview i.e. cross-line representations cannot be affected (for example, apply transforms to them) - all that can be changed is a subview transform.- Zoom and pan transforms are saved only in the global
matrix . matrix then used to calculate the coordinates of the section drawn in the superview (trivial), as well as the subview transformation matrix (not trivial - the reasons for this question).- Since it is not possible to unambiguously derive the matrix from
subview from the global matrix, it allows you to store additional data in variables, which are then used together with the global matrix to calculate the subview matrix.
- The size / origin of the
container may change during zooming / panning. The reason for this is that the y-axis labels can have different lengths, and the chart must dynamically adapt the size of the content to the space occupied by the labels (during zooming and panning). - Of course, when the size of the
container changes, the ratio of the domain-screen coordinates should change accordingly, so that the full source visible domain continues to be contained in the container . For example, if I show the x axis with the domain [0, 10] in a container frame with a width of 500pt, that is, the ratio for converting the domain point to screen coordinates is 500/10 500/10=50 and reduces the width of the container to 250, now my domain [0, 10], which should correspond to this new width, has a ratio of 25. - It should work for several crosses (at the same time) and for arbitrary places for each domain. This should happen automatically, allowing 1-7, but mentioning it for completeness.
What I've done:
Here are the walkthroughs I made to better understand the problem:
Step 1 (works):
Create a hierarchy as described at the beginning, not displaying anything but the intersections that should remain aligned during the (software) zooming and panning. Conforms to requirements 1, 2, 3, 4 and 5:
Gist with a playground.
Features here:
- I skipped the
container view to make it simple. subview is a direct subtext of superview . subview is the same size as superview (before scaling), and also to simplify its use.- I set the
subview reference point to the origin (0, 0), which seems necessary to synchronize with the global matrix. - You must replace the translation used to change the binding in order to apply it again with the global matrix. Otherwise, it will be overwritten. For this, I use the variable
subviewAnchorTranslation . This applies to the additional data that I had in mind in the pool at requirement 5.
Well, as you can see, everything works here. It's time to try the next step.
Step 2 (works):
Copy of the playing field with steps 1 with changes:
- A
container view has been added, now resembling the hierarchy of views described at the beginning. - For a
subview , which is now a container subview to continue displaying at the same position, it should be moved to the beginning and to the left by -container.origin . - Now the zoom and pan calls are randomly alternated with calls to change the position / frame size in the container.
Crosses continue to sync. Requirements met: all from step 1 + requirement 6.
Gist with a playground
Step 3 (does not work):
So far I have been working with a range of screen that starts at 0 (to the left of the visible area result). This means that container does not perform this function to contain a range, i.e. Requirement 7. To comply with this, the origin of the container must be included in the calculation of the relation.
Now also subview needs to be scaled to fit in the container / display the cross in the right place. Which adds a second variable (first it is subviewAnchorTranslation ), which I called contentScalingFactor , which contains this scaling, which should be included in the calculation of the subview matrix.
Here I did some experiments, they all failed. In the current state, the subview starts with the same frame as the container , and its frame is adjusted + scaled when the container frame changes. In addition, subview now inside the container, i.e. Its origin is now container origin, not superview origin, I have to install an update of my anchor so that the origin is not (0,0), but (-x, -y), being the x and y coordinates of the origin of the container , such that subview continues to be located relative to the origin of superview . And itโs logical to update this anchor every time the container changes its origin, since it changes the relative position from content origin to the start of superview .
I downloaded the code for this - in this case, the full iOS project, and not just the playground (at first I thought it worked and wanted to test using real gestures). In the actual project, I am working on the conversion better, but I could not find the difference. In any case, this does not work, at some point there are always slight offsets, and the dots / crosses go out of sync.
Github project
Itโs good how I solve it so that all conditions are fulfilled. Crosses should remain in sync, with continuous zooming / panning and changing the container frame between them.