swiftxcodeuikituigraphicsimagerenderer

Resize Image in stackView, keep aspect-ratio


I want to clone the Apple Maps App UIButtons, they look like this:

screenshot Apple Maps app

This is my Code:

 class CustomView: UIImageView {
    init(frame: CGRect, corners: CACornerMask, systemName: String) {
        super.init(frame: frame)
        self.createBorders(corners: corners)
        self.createImage(systemName: systemName)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func createBorders(corners: CACornerMask) {
        self.contentMode = .scaleAspectFill
        self.clipsToBounds = false
        self.layer.cornerRadius = 10
        self.layer.maskedCorners = corners
        self.layer.masksToBounds = false
        self.layer.shadowOffset = .zero
        self.layer.shadowColor = UIColor.gray.cgColor
        self.layer.shadowRadius = 10
        self.layer.shadowOpacity = 0.2
        self.backgroundColor = .white
        let shadowAmount: CGFloat = 2
        let rect = CGRect(x: 0, y: 2, width: self.bounds.width + shadowAmount * 0.1, height: self.bounds.height + shadowAmount * 0.1)
        self.layer.shadowPath = UIBezierPath(rect: rect).cgPath

    }
    
    func createImage(systemName: String) {
        let image = UIImage(systemName: systemName)!
//        let renderer = UIGraphicsImageRenderer(bounds: self.frame)
//        let renderedImage = renderer.image { (_) in
//            image.draw(in: frame.insetBy(dx: 16, dy: 30))
//        }
//      self.image = renderedImage.withRenderingMode(.alwaysTemplate)
        self.image = image
    }
}

And used it like this:

 let size = CGSize(width: 10, height: 10)
    let frame = CGRect(origin: CGPoint(x: 360, y: 45), size: size)
    let infoView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMaxXMinYCorner], systemName: "info.circle")
    let locationView = CustomView(frame: frame, corners: [], systemName: "location")
    let plusView = CustomView(frame: frame, corners: [.layerMinXMaxYCorner, .layerMaxXMaxYCorner], systemName: "plus.circle")
    let binocularsView = CustomView(frame: frame, corners: [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMinYCorner, .layerMaxXMaxYCorner], systemName: "binoculars.fill")
    let stackView = UIStackView(arrangedSubviews: [infoView, locationView, plusView, binocularsView])
    stackView.frame = CGRect(origin: CGPoint(x: 360, y: 45), size: CGSize(width: 45, height: 180))
    stackView.distribution = .fillEqually
    stackView.axis = .vertical
    stackView.setCustomSpacing(3, after: plusView)
    view.addSubview(stackView)

With this code, it looks like this:

screenshot BEFORE

As you can see, the icons are too big... So this is what I tried to shrink them: (uncommented the lines in createImage())

func createImage(systemName: String) {
    let image = UIImage(systemName: systemName)!
    let renderer = UIGraphicsImageRenderer(bounds: self.frame)
    let renderedImage = renderer.image { (_) in
        image.draw(in: frame.insetBy(dx: 16, dy: 30))
    }
  self.image = renderedImage.withRenderingMode(.alwaysTemplate)
}

But then its distorted...

Does can help me? :)

Thank you!!!!


Solution

  • Rather than subclassing UIImageView subclass UIView and add a UIImageView as a subview. Then you can create as much spacing around the image as you want.

    class CustomView: UIView {
    
        lazy var imageView: UIImageView = {
            let imageView = UIImageView()
            imageView.translatesAutoresizingMaskIntoConstraints = false
            return imageView
        }()
    
        init(frame: CGRect, corners: CACornerMask, systemName: String) {
            super.init(frame: frame)
            self.setUpViews()
            // ...
        }
    
        func setUpViews() {
            addSubview(imageView)
            NSLayoutConstraint.activate([
                imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 5),
                imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -5),
                imageView.topAnchor.constraint(equalTo: topAnchor, constant: 5),
                imageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5),
            ])
        }
    
        func createImage(systemName: String) {
            let image = UIImage(systemName: systemName)!
            self.imageView.image = image
        }
    }