skip to Main Content

I have a UIView with a UIImageView as subview added, the UIImageView is a texture that repeats. The UIView width and height are correct, but the image is out of the size. I added the ClipsToBounds, but it’s not clipping the image at all. Is there a specific order or what am I doing wrong the image is not clipped inside it’s parent view?

let rectangleView = UIView(frame: CGRect(x: x, y: y, width: width, height: height))
rectangleView.isUserInteractionEnabled = false

if let texturesUrl = layout.Url, let url = texturesUrl.isValidURL() ? URL(string: texturesUrl) : URL(string: String(format: AppManager.shared.baseTexturesUrl, texturesUrl)) {
     let widthLimit = scale * CGFloat(layout.Width ?? 0)
     let heightLimit = scale * CGFloat(layout.Height ?? 0)
     let widthStep = scale * CGFloat(layout.TileWidth ?? layout.Width ?? 0)
     let heightStep = scale * CGFloat(layout.TileHeight ?? layout.Height ?? 0)

     var locY = CGFloat(0)
     let size = CGSize(width: widthStep, height: heightStep)

     if widthLimit > 0, heightLimit > 0 {
          while locY < heightLimit {
              var locX = CGFloat(0)
                  while locX < widthLimit {
                      let imageView = UIImageView()
                      rectangleView.addSubview(imageView)
                      imageView.contentMode = .scaleAspectFill
                      imageView.translatesAutoresizingMaskIntoConstraints = false
                      imageView.clipsToBounds = true
                      imageView.isUserInteractionEnabled = false
                      imageView.anchor(top: rectangleView.topAnchor, leading: rectangleView.leadingAnchor, bottom: nil, trailing: nil, padding: UIEdgeInsets(top: locY, left: locX, bottom: 0, right: 0), size: size)
                      imageView.setImage(with: url, size: size)
                      locX += widthStep
                  }
                  locY += heightStep
              }
          }
     }

2

Answers


  1. You don’t need to add so many image views, just use it as a repeating background:

    rectangleView.backgroundColor = UIColor(patternImage: myImage)
    

    See documentation for UIColor(patternImage:).

    Login or Signup to reply.
  2. You can do this much more efficiently with CAReplicatorLayer.

    Here’s a quick example:

    class TileExampleViewController: UIViewController {
        
        let tiledView = UIView()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            tiledView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(tiledView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                tiledView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
                tiledView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                tiledView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
                tiledView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            ])
            
        }
        
        override func viewDidLayoutSubviews() {
            // we want to do this here, when we know the
            //  size / frame of the tiledView
            
            // make sure we can load the image
            guard let tileImage = UIImage(named: "tileSquare") else { return }
            
            // let's just pick 80 x 80 for the tile size
            let tileSize: CGSize = CGSize(width: 80.0, height: 80.0)
    
            // create a "horizontal" replicator layer
            let hReplicatorLayer = CAReplicatorLayer()
            hReplicatorLayer.frame.size = tiledView.frame.size
            hReplicatorLayer.masksToBounds = true
            
            // create a "vertical" replicator layer
            let vReplicatorLayer = CAReplicatorLayer()
            vReplicatorLayer.frame.size = tiledView.frame.size
            vReplicatorLayer.masksToBounds = true
            
            // create a layer to hold the image
            let imageLayer = CALayer()
            imageLayer.contents = tileImage.cgImage
            imageLayer.frame.size = tileSize
            
            // add the imageLayer to the horizontal replicator layer
            hReplicatorLayer.addSublayer(imageLayer)
            
            // add the horizontal replicator layer to the vertical replicator layer
            vReplicatorLayer.addSublayer(hReplicatorLayer)
    
            // how many "tiles" do we need to fill the width
            let hCount = tiledView.frame.width / tileSize.width
            hReplicatorLayer.instanceCount = Int(ceil(hCount))
            
            // Shift each image instance right by tileSize width
            hReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(
                tileSize.width, 0, 0
            )
            
            // how many "rows" do we need to fill the height
            let vCount = tiledView.frame.height / tileSize.height
            vReplicatorLayer.instanceCount = Int(ceil(vCount))
            
            // shift each "row" down by tileSize height
            vReplicatorLayer.instanceTransform = CATransform3DMakeTranslation(
                0, tileSize.height, 0
            )
    
            // add the vertical replicator layer as a sublayer
            tiledView.layer.addSublayer(vReplicatorLayer)
    
        }
        
    }
    

    I used this tile image:

    enter image description here

    and we get this result with let tileSize: CGSize = CGSize(width: 80.0, height: 80.0):

    enter image description here

    with let tileSize: CGSize = CGSize(width: 120.0, height: 160.0):

    enter image description here

    with let tileSize: CGSize = CGSize(width: 40.0, height: 40.0):

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search