I have a scroll view that zooms only horizontally on pinch (based on this answer)
The content view has several children that are evenly spaced horizontally, and placed at different heights. Basically, I'm plotting a graph with dots.
Each marker is a custom UIView subclass that has a label as subview and draws a red circle inside drawRect(_:)
.
(The scroll view and its only child the content view use Autolayout, but all subviews of the content view are placed with frames calculated at runtime; no constraints except the label positioning respect to the marker)
I have modified the answer linked above, so that -when zooming- the dots get more spaced horizontally, but stay the same size.
This is part of the code for my content view:
override var transform: CGAffineTransform {
get {
return super.transform
}
set {
if let unzoomedViewHeight = unzoomedViewHeight {
// ^Initial height of the content view, captured
// on layoutSubviews() and used to calculate a
// a zoom transform that preserves the height
// (see linked answer for details)
// 1. Ignore vertical zooming for this view:
var modified = newValue
modified.d = 1.0
modified.ty = (1.0 - modified.a) * unzoomedViewHeight/2
super.transform = modified
// 2. Ignore zooming altogether in marker subviews:
var markerTransform = CGAffineTransformInvert(newValue)
markerTransform.d = 1.0 // No y-scale
for subview in subviews where subview is MarkerView {
subview.transform = markerTransform
}
}
}
}
Next, I want to connect my dots with straight line segments. The problem is, when I zoom the scroll view the segments become blurry.
In addition to the blurring, and because I am only zooming horizontally, the segments "shear" a bit (depending on their their slope), and thus the line width becomes uneven:
(Right now I am placing an intermediate view between each pair of dots, that draws a single segment - but the result is the same with a single path)
I have tried making the segment-drawing views aware of the transform that is applied to them by their parent, revert it, and instead modify their frame accordingly (and redraw the path within the new bounds); however it doesn't seem to work.
What is the best way to draw zoom-resistant bezier paths inside a scroll view?
I got rid of the pixelation by following this answer to a similar question (almost duplicate?); in my case the code translates to:
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView?, atScale scale: CGFloat)
{
for segment in contentView.subviews where segment is SegmentView {
segment.layer.contentsScale = scale
segment.setNeedsDisplay()
}
}
...However, I still have the uneven line width issue due to stretching the (diagonal) segments only in the horizontal direction.
My initial thought, having not done this before exactly, is to store the graph as a bezier path rather than as a set of view line and point views, and based on a single unit axis. You could convert your data into this form just for drawing.
So, all points on the bezier path are normalised into the range 0 to 1.
Once you've done that you can apply a transform to the bezier path to translate (move) and scale (zoom) to the part you want and then draw the bezier path at full resolution.
This will work and is different to your current situation because your current code draws the views and then scales then so you get artifacts. The above option scales the path and then draws it.