Home > Enterprise >  Need to make a grid this on UIKit using a UICollectionView
Need to make a grid this on UIKit using a UICollectionView

Time:11-02

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. enter image description here

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):

enter image description here


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:

enter image description here

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:

enter image description here

and after zooming in a bit:

enter image description here

  • Related