I want to make a grid in the photo. How can I achieve this? Can you tell me where to look? Googled everything but found nothing, except through CoreGraphics, but that's not an option I'm considering.
CodePudding user response:
Here's a really quick example using a CAShapeLayer
...
UIImageView subclass
class GridImageView: UIImageView {
public var numRows: Int = 1
public var numColumns: Int = 1
private let gridLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
override init(image: UIImage?) {
super.init(image: image)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
layer.addSublayer(gridLayer)
gridLayer.strokeColor = UIColor.white.cgColor
gridLayer.fillColor = UIColor.clear.cgColor
gridLayer.lineWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
if numRows == 1 && numColumns == 1 {
// no need to draw any lines
gridLayer.path = nil
return
}
let bez = UIBezierPath()
if numRows > 1 {
let yIncrement: CGFloat = bounds.height / CGFloat(numRows)
var y: CGFloat = yIncrement
for _ in 0..<numRows-1 {
bez.move(to: CGPoint(x: bounds.minX, y: y))
bez.addLine(to: CGPoint(x: bounds.maxX, y: y))
y = yIncrement
}
}
if numColumns > 1 {
let xIncrement: CGFloat = bounds.width / CGFloat(numColumns)
var x: CGFloat = xIncrement
for _ in 0..<numColumns-1 {
bez.move(to: CGPoint(x: x, y: bounds.minY))
bez.addLine(to: CGPoint(x: x, y: bounds.maxY))
x = xIncrement
}
}
gridLayer.path = bez.cgPath
}
}
Example Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
guard let img = UIImage(named: "beach") else { return }
let imgView = GridImageView(image: img)
imgView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(imgView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
imgView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
imgView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
imgView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: img.size.height / img.size.width)
])
imgView.numRows = 4
imgView.numColumns = 3
}
}
Output (using a random beach jpg):
Edit - after further comments on OP...
To get the "effect" from the video you posted, we can simply add a "grid overlay view" on top of the scroll view.
So, we'll use an almost identical subclassed UIView
:
class GridOverlayView: UIView {
public var lineColor: UIColor = .white { didSet { setNeedsLayout() } }
public var numRows: Int = 1 { didSet { setNeedsLayout() } }
public var numColumns: Int = 1 { didSet { setNeedsLayout() } }
private let gridLayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
layer.addSublayer(gridLayer)
gridLayer.fillColor = UIColor.clear.cgColor
gridLayer.lineWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
if numRows == 1 && numColumns == 1 {
// no need to draw any lines
gridLayer.path = nil
return
}
let bez = UIBezierPath()
if numRows > 1 {
let yIncrement: CGFloat = bounds.height / CGFloat(numRows)
var y: CGFloat = yIncrement
for _ in 0..<numRows-1 {
bez.move(to: CGPoint(x: bounds.minX, y: y))
bez.addLine(to: CGPoint(x: bounds.maxX, y: y))
y = yIncrement
}
}
if numColumns > 1 {
let xIncrement: CGFloat = bounds.width / CGFloat(numColumns)
var x: CGFloat = xIncrement
for _ in 0..<numColumns-1 {
bez.move(to: CGPoint(x: x, y: bounds.minY))
bez.addLine(to: CGPoint(x: x, y: bounds.maxY))
x = xIncrement
}
}
gridLayer.strokeColor = lineColor.cgColor
gridLayer.path = bez.cgPath
}
}
with this "beach" image:
and this controller class with a scroll view:
class GridScrollTestVC: UIViewController, UIScrollViewDelegate {
let imgView = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemYellow
guard let img = UIImage(named: "beach") else { return }
imgView.image = img
let scrollView = UIScrollView()
scrollView.backgroundColor = .red
scrollView.delegate = self
imgView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
scrollView.addSubview(imgView)
let g = view.safeAreaLayoutGuide
let cg = scrollView.contentLayoutGuide
let fg = scrollView.frameLayoutGuide
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
scrollView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
scrollView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
scrollView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
imgView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 0.0),
imgView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 0.0),
imgView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0.0),
imgView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: 0.0),
imgView.widthAnchor.constraint(equalTo: fg.widthAnchor),
imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor, multiplier: img.size.height / img.size.width)
])
scrollView.minimumZoomScale = 1.0
scrollView.maximumZoomScale = 5.0
// now let's add the grid "overlay"
let gridView = GridOverlayView()
gridView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(gridView)
gridView.numRows = 4
gridView.numColumns = 3
gridView.lineColor = .black
NSLayoutConstraint.activate([
gridView.topAnchor.constraint(equalTo: scrollView.topAnchor),
gridView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
gridView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
gridView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
])
// don't let the grid overlay view interfere with the scrolling/zooming
gridView.isUserInteractionEnabled = false
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
}
and we get this output:
and after zooming in a bit: