I’ve two scrollviews aspect by aspect. Each are set identical however the left one does’nt present the content material in it. Once I use simply one after the other it really works tremendous. However once I use each on the identical time, it doesn’t work. You may run it, simply give ComplexVC to a view controller. There are two scrollviews in a view controller and two completely different UIView courses which can be every linked to 1 scrollview.
import UIKit
class DrawZoomBaseVC: UIViewController {
//let container : UIView = UIView()
let scrollView: UIScrollView = UIScrollView()
let scrollViewleft: UIScrollView = UIScrollView()
// this might be a plain, clear UIView that we are going to use
// because the viewForZooming
let zoomView = UIView()
let zoomViewleft = UIView()
// this might be positioned *behind* the scrollView
// in our subclasses, we'll set it to both
// Easy or Advanced
// and we'll set its zoomScale and contentOffset
// to match the scrollView
var drawView: UIView!
var drawViewleft: UIView!
// a label to place on the high to indicate the present zoomScale
let infoLabel: UILabel = {
let v = UILabel()
v.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
v.textAlignment = .heart
v.numberOfLines = 0
v.textual content = "nnn"
return v
}()
override func viewDidLoad() {
tremendous.viewDidLoad()
view.backgroundColor = UIColor.yellow
[infoLabel,drawViewleft, drawView, scrollView,scrollViewleft].forEach { v in
v!.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v!)
}
zoomView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(zoomView)
zoomViewleft.translatesAutoresizingMaskIntoConstraints = false
scrollViewleft.addSubview(zoomViewleft)
drawView.backgroundColor = .black
scrollView.backgroundColor = .clear
zoomView.backgroundColor = .clear
scrollViewleft.backgroundColor = .orange
let g = view.safeAreaLayoutGuide
let cg = scrollView.contentLayoutGuide
let cgl = scrollViewleft.contentLayoutGuide
NSLayoutConstraint.activate([
// info label at the top
infoLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
infoLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
infoLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.topAnchor.constraint(equalTo: infoLabel.bottomAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: scrollViewleft.trailingAnchor, constant: 0.0),
scrollView.trailingAnchor.constraint(equalTo:g.trailingAnchor, constant: -20.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
scrollViewleft.topAnchor.constraint(equalTo: infoLabel.bottomAnchor, constant: 20.0),
scrollViewleft.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollViewleft.trailingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0.0),
scrollViewleft.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
scrollViewleft.widthAnchor.constraint(equalToConstant: 120.0),
zoomView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 0.0),
zoomView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0.0),
zoomView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0.0),
zoomView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: 0.0),
drawView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0.0),
drawView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0.0),
drawView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: 0.0),
drawView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0.0),
zoomViewleft.topAnchor.constraint(equalTo: cgl.topAnchor, constant: 0.0),
zoomViewleft.leadingAnchor.constraint(equalTo: cgl.leadingAnchor, constant: 0.0),
zoomViewleft.trailingAnchor.constraint(equalTo: cgl.trailingAnchor, constant: 0.0),
zoomViewleft.bottomAnchor.constraint(equalTo: cgl.bottomAnchor, constant: 0.0),
drawViewleft.topAnchor.constraint(equalTo: scrollViewleft.topAnchor, constant: 0.0),
drawViewleft.leadingAnchor.constraint(equalTo: scrollViewleft.leadingAnchor, constant: 0.0),
drawViewleft.trailingAnchor.constraint(equalTo: scrollViewleft.trailingAnchor, constant: 0.0),
drawViewleft.bottomAnchor.constraint(equalTo: scrollViewleft.bottomAnchor, constant: 0.0),
])
scrollView.maximumZoomScale = 60.0
scrollView.minimumZoomScale = 0.1
scrollView.zoomScale = 1.0
scrollViewleft.maximumZoomScale = 60.0
scrollViewleft.minimumZoomScale = 0.1
scrollViewleft.zoomScale = 1.0
scrollView.indicatorStyle = .white
scrollViewleft.indicatorStyle = .white
scrollView.delegate = self
scrollViewleft.delegate = self
infoLabel.isHidden = false
}
override func viewDidAppear(_ animated: Bool) {
tremendous.viewDidAppear(animated)
// if we're utilizing the ComplexDrawScaledView
// we *get* its dimension that was decided by
// it laying out its parts in its commonInit()
if let dv = drawView as? ComplexDrawScaledView {
zoomView.widthAnchor.constraint(equalToConstant: dv.virtualSize.width).isActive = true
zoomView.heightAnchor.constraint(equalToConstant: dv.virtualSize.top).isActive = true
}
if let dvl = drawView as? ComplexDrawScaledViewleft {
zoomViewleft.widthAnchor.constraint(equalToConstant: dvl.virtualSize.width).isActive = true
zoomViewleft.heightAnchor.constraint(equalToConstant: dvl.virtualSize.top).isActive = true
}
// let auto-layout dimension the view earlier than we replace the data label
DispatchQueue.predominant.async {
self.updateInfoLabel()
}
}
func updateInfoLabel() {
infoLabel.textual content = String(format: "nzoomView dimension: (%0.0f, %0.0f)nzoomScale: %0.3fn", zoomView.body.width, zoomView.body.top, scrollView.zoomScale)
}
}
extension DrawZoomBaseVC: UIScrollViewDelegate {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if let dvl = drawViewleft as? ComplexDrawScaledViewleft {
dvl.contentOffset = scrollViewleft.contentOffset
}
if let dv = drawView as? ComplexDrawScaledView {
dv.contentOffset = scrollView.contentOffset
}
}
func scrollViewDidZoom(_ scrollView: UIScrollView) {
updateInfoLabel()
if let dvl = drawViewleft as? ComplexDrawScaledViewleft {
dvl.zoomScale = scrollViewleft.zoomScale
}
if let dv = drawView as? ComplexDrawScaledView {
dv.zoomScale = scrollView.zoomScale
}
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
if scrollView == self.scrollView {
return zoomView
} else if scrollView == self.scrollViewleft {
return zoomViewleft
}
return nil
}
}
class ComplexVC: DrawZoomBaseVC {
override func viewDidLoad() {
drawView = ComplexDrawScaledView()
drawViewleft = ComplexDrawScaledViewleft()
tremendous.viewDidLoad()
}
}
class ComplexDrawScaledView: UIView {
// this might be set by the "rects" structure in commonInit()
public var virtualSize: CGSize = .zero
public var zoomScale: CGFloat = 1.0 { didSet { setNeedsDisplay() } }
public var contentOffset: CGPoint = .zero { didSet { setNeedsDisplay() } }
personal let nCols: Int = 10
personal let nRows: Int = 20
personal let colWidth: CGFloat = 120.0
personal let rowHeight: CGFloat = 80.0
personal let colSpacing: CGFloat = -2.0
personal let rowSpacing: CGFloat = -2.0
personal let rectInset: CGSize = .init(width: 1.0, top: 1.0)
personal var theRectPaths: [UIBezierPath] = []
override init(body: CGRect) {
tremendous.init(body: body)
commonInit()
}
required init?(coder: NSCoder) {
tremendous.init(coder: coder)
commonInit()
}
personal func commonInit() {
// let's create a "grid" of rects
// each rect might be used to create a
// rect path - alternating between rect and roundedRect
// a centered oval path
// and a centered textual content level
var r: CGRect = .init(x: 0.0, y: 0.0, width: colWidth, top: rowHeight)
for _ in 0..<nRows {
for _ in 0..<nCols {
let rPath = UIBezierPath(rect: r.insetBy(dx: rectInset.width, dy: rectInset.top))
theRectPaths.append(rPath)
r.origin.x += colWidth + colSpacing
}
r.origin.x = 0.0
r.origin.y += rowHeight + rowSpacing
}
// our "digital dimension"
let w: CGFloat = theRectPaths.compactMap( { $0.bounds.maxX }).max()!
let h: CGFloat = theRectPaths.compactMap( { $0.bounds.maxY }).max()!
let sz: CGSize = .init(width: w, top: h)
let v: CGFloat = 100.0
r = .init(x: 0.0, y: 0.0, width: v, top: v)
virtualSize = sz
}
override func draw(_ rect: CGRect) {
let tr = CGAffineTransform(translationX: -contentOffset.x, y: -contentOffset.y)
.scaledBy(x: zoomScale, y: zoomScale)
drawRects(insideRect: rect, withTransform: tr)
}
personal func drawRects(insideRect: CGRect, withTransform tr: CGAffineTransform) {
UIColor.inexperienced.setStroke()
theRectPaths.forEach { pth in
if let path = pth.copy() as? UIBezierPath {
// rework a replica of the trail
path.apply(tr)
// solely draw if seen
if path.bounds.intersects(insideRect) {
path.lineWidth = 1.0 * zoomScale
path.stroke()
}
}
}
}
}
class ComplexDrawScaledViewleft: UIView {
// this might be set by the "rects" structure in commonInit()
public var virtualSize: CGSize = .zero
public var zoomScale: CGFloat = 1.0 { didSet { setNeedsDisplay() } }
public var contentOffset: CGPoint = .zero { didSet { setNeedsDisplay() } }
personal let nCols: Int = 10
personal let nRows: Int = 20
personal let colWidth: CGFloat = 120.0
personal let rowHeight: CGFloat = 80.0
personal let colSpacing: CGFloat = -2.0
personal let rowSpacing: CGFloat = -2.0
personal let rectInset: CGSize = .init(width: 1.0, top: 1.0)
personal var theRectPaths: [UIBezierPath] = []
override init(body: CGRect) {
tremendous.init(body: body)
commonInit()
}
required init?(coder: NSCoder) {
tremendous.init(coder: coder)
commonInit()
}
personal func commonInit() {
// let's create a "grid" of rects
// each rect might be used to create a
// rect path - alternating between rect and roundedRect
// a centered oval path
// and a centered textual content level
var r: CGRect = .init(x: 0.0, y: 0.0, width: colWidth, top: rowHeight)
for _ in 0..<nRows {
for _ in 0..<nCols {
let rPath = UIBezierPath(rect: r.insetBy(dx: rectInset.width, dy: rectInset.top))
theRectPaths.append(rPath)
r.origin.x += colWidth + colSpacing
}
r.origin.x = 0.0
r.origin.y += rowHeight + rowSpacing
}
// our "digital dimension"
let w: CGFloat = theRectPaths.compactMap( { $0.bounds.maxX }).max()!
let h: CGFloat = theRectPaths.compactMap( { $0.bounds.maxY }).max()!
let sz: CGSize = .init(width: w, top: h)
let v: CGFloat = 100.0
r = .init(x: 0.0, y: 0.0, width: v, top: v)
virtualSize = sz
}
override func draw(_ rect: CGRect) {
let tr = CGAffineTransform(translationX: -contentOffset.x, y: -contentOffset.y)
.scaledBy(x: zoomScale, y: zoomScale)
drawRects(insideRect: rect, withTransform: tr)
}
personal func drawRects(insideRect: CGRect, withTransform tr: CGAffineTransform) {
UIColor.white.setStroke()
theRectPaths.forEach { pth in
if let path = pth.copy() as? UIBezierPath {
// rework a replica of the trail
path.apply(tr)
// solely draw if seen
if path.bounds.intersects(insideRect) {
path.lineWidth = 1.0 * zoomScale
path.stroke()
}
}
}
}
}