Skip to content

Instantly share code, notes, and snippets.

@ivnsch
Created December 26, 2016 21:58
Show Gist options
  • Save ivnsch/c8da84ca5f2b4064f961a69f4407c2e6 to your computer and use it in GitHub Desktop.
Save ivnsch/c8da84ca5f2b4064f961a69f4407c2e6 to your computer and use it in GitHub Desktop.
//: Playground 2: Same as 1 with added container view and "random" changes of its size/position between zoom/pan. Everything works
import UIKit
import PlaygroundSupport
let domainPoint = CGPoint(x: 5, y: 5)
// The following variables are simplified ranges - all start with 0
let xDomain: CGFloat = 10
let yDomain: CGFloat = 10
let xScreenRange: CGFloat = 500
let yScreenRange: CGFloat = 500
func toScreenPoint(domainPoint: CGPoint) -> CGPoint {
return CGPoint(x: domainPoint.x * xScreenRange / xDomain, y: domainPoint.y * yScreenRange / yDomain)
}
func toDomain(screenPoint: CGPoint) -> CGPoint {
return CGPoint(x: screenPoint.x * xDomain / xScreenRange, y: screenPoint.y * yDomain / yScreenRange)
}
extension UIView {
// Changes the anchor of view without changing its position. Returns translation in pt
func setAnchorWithoutTranslation(anchor: CGPoint) -> CGPoint {
let offsetAnchor = CGPoint(x: anchor.x - layer.anchorPoint.x, y: anchor.y - layer.anchorPoint.y)
let offset = CGPoint(x: frame.width * offsetAnchor.x, y: frame.height * offsetAnchor.y)
layer.anchorPoint = anchor
transform = transform.translatedBy(x: offset.x, y: offset.y)
return offset
}
}
var screenPoint: CGPoint {
return toScreenPoint(domainPoint: domainPoint)
}
// The transform matrix to be applied to drawed lines in parent and to subview
var matrix = CGAffineTransform.identity
class MySuperview: UIView {
override func draw(_ rect: CGRect) {
let transformed: CGPoint = CGPoint(x: screenPoint.x, y: screenPoint.y).applying(matrix)
// Draw "cross" on superview
let path = UIBezierPath()
path.move(to: CGPoint(x: transformed.x, y: 0))
path.addLine(to: CGPoint(x: transformed.x, y: 1000))
path.close()
UIColor.black.set()
path.stroke()
let path2 = UIBezierPath()
path2.move(to: CGPoint(x: 0, y: transformed.y))
path2.addLine(to: CGPoint(x: 1000, y: transformed.y))
path2.close()
UIColor.black.set()
path2.stroke()
}
}
let root = UIView()
root.frame = CGRect(x: 0, y: 0, width: 600, height: 1000)
root.backgroundColor = UIColor.white
PlaygroundPage.current.liveView = root
let superview = MySuperview()
superview.frame = CGRect(x: 100, y: 100, width: xScreenRange, height: yScreenRange)
superview.backgroundColor = UIColor.orange.withAlphaComponent(0.3)
root.addSubview(superview)
let container = UIView()
container.frame = superview.bounds.insetBy(dx: 100, dy: 100)
container.backgroundColor = UIColor.green.withAlphaComponent(0.4)
superview.addSubview(container)
let subview = UIView()
subview.frame = CGRect(x: -container.frame.minX, y: -container.frame.minY, width: superview.bounds.width, height: superview.bounds.height)
subview.backgroundColor = UIColor.blue.withAlphaComponent(0.5)
container.addSubview(subview)
// Draw "cross" on subview (by adding subviews)
let contentLine = UIView()
contentLine.backgroundColor = UIColor.red.withAlphaComponent(0.4)
let contentLineWidth: CGFloat = 5
contentLine.frame = CGRect(x: screenPoint.x - contentLineWidth / 2, y: 0, width: contentLineWidth, height: 1000)
subview.addSubview(contentLine)
let contentLine2 = UIView()
contentLine2.backgroundColor = UIColor.red.withAlphaComponent(0.4)
contentLine2.frame = CGRect(x: 0, y: screenPoint.y - contentLineWidth / 2, width: 1000, height: contentLineWidth)
subview.addSubview(contentLine2)
// Set anchor point to (0, 0) to "sync" coordinate system with matrix
// Also, store the translation which has to preserve position while changing anchor, to use it to define subview's matrix in applyMatrixToSubview
let subviewAnchorTranslation = subview.setAnchorWithoutTranslation(anchor: CGPoint(x: 0, y: 0))
let newViewTransform = subview.transform
func zoom(center: CGPoint, level: CGPoint) {
matrix = matrix.translatedBy(x: center.x, y: center.y)
matrix.a = level.x
matrix.d = level.y
matrix = matrix.translatedBy(x: -center.x, y: -center.y)
superview.setNeedsDisplay()
print("subview trans before apply: \(subview.transform), matrix: \(matrix)")
applyMatrixToSubview()
print("subview trans after apply: \(subview.transform)")
}
func zoom(center: CGPoint, delta: CGPoint) {
zoom(center: center, level: CGPoint(x: matrix.a * delta.x, y: matrix.d * delta.y))
}
func applyMatrixToSubview() {
let m = CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: subviewAnchorTranslation.x, ty: subviewAnchorTranslation.y)
subview.transform = matrix.concatenating(m)
}
func translate(delta: CGPoint) {
matrix.tx = matrix.tx + delta.x
matrix.ty = matrix.ty + delta.y
applyMatrixToSubview()
}
zoom(center: screenPoint, delta: CGPoint(x: 2, y: 2))
zoom(center: screenPoint, delta: CGPoint(x: 1.2, y: 1.2))
translate(delta: CGPoint(x: 70, y: 70))
////zoom(center: screenPoint, delta: CGPoint(x: 2.1, y: 2))
zoom(center: CGPoint(x: 200, y: 200), delta: CGPoint(x: 2.1, y: 2))
zoom(center: CGPoint(x: 50, y: 70), delta: CGPoint(x: 0.7, y: 0.6))
translate(delta: CGPoint(x: -10, y: 0))
func changeContainerFrame(delta: CGPoint) {
container.frame = CGRect(x: container.frame.minX + delta.x, y: container.frame.minY + delta.y, width: container.frame.width - 20, height: container.frame.height - 20)
subview.frame = CGRect(x: subview.frame.minX - delta.x, y: subview.frame.minY - delta.y, width: subview.frame.width, height: subview.frame.height)
}
changeContainerFrame(delta: CGPoint(x: 30, y: 30))
zoom(center: CGPoint(x: 60, y: 70), delta: CGPoint(x: 2.3, y: 2.2))
translate(delta: CGPoint(x: -760, y: -490))
changeContainerFrame(delta: CGPoint(x: 90, y: 50))
zoom(center: CGPoint(x: 200, y: 170), delta: CGPoint(x: 1.1, y: 1.1))
changeContainerFrame(delta: CGPoint(x: -190, y: -150))
zoom(center: CGPoint(x: 200, y: 170), delta: CGPoint(x: 1.1, y: 1.1))
translate(delta: CGPoint(x: -240, y: -250))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment