1

I have a bunch of CAShapeLayers built with UIBezierPath and stored inside of UIScrollView.

I wonder if there any way to keep the lineWidth of CAShapeLayer's path away from scaling when UIScrollView is zoomed over 100%. I mean applying the UIScrollView's transformation rules to CAShapeLayer, but keep the path's widh the same as it used to be on 1.0 zoomScale. (So when the layer with 1pt path lineWidth will be zoomed to 300%, the lineWidth of the layer on @2x device won't become 6px height, but will keep the height of 2px)

autobot
  • 139
  • 9
  • What about setting the lineWidth accordingly to the zoom of your scrollView ? Something like ‘lineWidth = lineWidthFor@x1Zoom * scrollView.zoomScale’ in ‘scrollViewDidZoom’ ? – GaétanZ Jan 15 '18 at 19:54
  • @GaétanZ lineWidth value doesn't seem to be changed when zoomed. It shows its original value when I output it into a log. I guess the problem is that scaling is applied by UIScrollView to the whole CAShapeLayer. And I need to separate them somehow. – autobot Jan 15 '18 at 20:47

1 Answers1

0
class ViewController : UIViewController, UIScrollViewDelegate {

    private lazy var scrollView = UIScrollView()
    private lazy var contentView = UIView()
    private lazy var shapeLayer = CAShapeLayer()
    private let lineWidth: CGFloat = 1

    override func loadView() {
        view = UIView()
        view.addSubview(scrollView)
        scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        scrollView.addSubview(contentView)
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 2.0
        scrollView.delegate = self
        contentView.backgroundColor = .red
        let viewSize = CGSize(width: 400, height: 400)
        scrollView.contentSize = viewSize
        contentView.frame = CGRect(x: 0, y: 0, width: viewSize.width, height: viewSize.height)
        setUpShapeLayer(in: viewSize)
    }

    //MARK: - UIScrollViewDelegate

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        shapeLayer.lineWidth = lineWidth * 1 / scrollView.zoomScale
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return contentView
    }

    //MARK: - Private

    private func setUpShapeLayer(in size: CGSize) {
        shapeLayer.fillColor = UIColor.white.cgColor
        shapeLayer.strokeColor = UIColor.black.cgColor
        shapeLayer.lineWidth = lineWidth
        contentView.layer.addSublayer(shapeLayer)
        let shapeSize = CGSize(width: 200, height: 200)
        let shapeBounds = CGRect(origin: .zero, size: shapeSize)
        shapeLayer.bounds.size = shapeSize
        shapeLayer.path = UIBezierPath(ovalIn: shapeBounds).cgPath
        shapeLayer.position = CGPoint(x: size.width / 2, y: size.height / 2)
    }
}

Is it what you want ?

GaétanZ
  • 4,870
  • 1
  • 23
  • 30
  • Not really. Here, the whole is layer resized (with the lineWidth as well). 1pt lineWidth still has a value of 1pt on zoomScale = 4.0. However, due to scaled layer, it becomes much thicker (on 4x zoom 1pt lineWidth is 8x8px for @2x devices). However, I'd like to keep it 2x2px, so the layer would be resized, but not its path's lineWidth. https://stackoverflow.com/questions/30434615/transforming-a-cashapelayer-to-a-specific-size-in-swift this looks like what I need, but couldn't make it work yet. – autobot Jan 17 '18 at 11:36
  • The problem in the code above is that it won't work with initial lineWidth values equal 1.0. As for higher values we always can reduce the lineWidth to smaller sizes. And from my experience it's not possible for 1.0, as it's already a minimal value. – autobot Jan 17 '18 at 11:43
  • 1
    Hm, yes you can. `lineWidth` is in point not pixel. I tried with lineWidth = 1. – GaétanZ Jan 17 '18 at 13:45
  • Hmm, tnx, I'll give it a try – autobot Jan 18 '18 at 10:33