0

Sometimes SF Symbols are rounded, but in opposite to SwiftUI's clipShape() procedure I can't reach a solution in Storyboard/Normal Swift. I've tried with

 func setCorner(radius: CGFloat) {
           layer.cornerRadius = radius
           clipsToBounds = true
    }
    
    func circleCorner() {
          superview?.layoutIfNeeded()
          setCorner(radius: frame.height / 2)
    }

but an ugly red ring remains like this:

enter image description here

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • *"ugly red ring remains"* ... are you trying to add a border to the symbol? Or, do you want it to be clear? – DonMag Jul 27 '22 at 12:41
  • Yes I want to get rid of it. If you look in the SF symbols there's just the white background. I set the tint color to red of the UIImage view and thats the result. I don't know how to crop the image so that the "filled" circle matches exactly the outer diameter of the symbol itself. – Frame Worker Jul 27 '22 at 19:08

1 Answers1

1

SF Symbols are designed to be used more like a font character - with "padding" on the sides - so we need to "remove" that padding.

Here is one approach by sub-classing UIImageView:

class FlagImageView: UIImageView {
    
    override func layoutSubviews() {
        super.layoutSubviews()

        let nm = "flag.circle.fill"
        
        // create SF Symbol image with point size equal to bounds height
        let cfg = UIImage.SymbolConfiguration(pointSize: bounds.height)
        guard let imgA = UIImage(systemName: nm, withConfiguration: cfg) else {
            fatalError("Could not load SF Symbol: \(nm)!")
        }
        // get a cgRef from imgA
        guard let cgRef = imgA.cgImage else {
            fatalError("Could not get cgImage!")
        }
        // create imgB from the cgRef
        //  this will remove the "padding"
        let imgB = UIImage(cgImage: cgRef, scale: imgA.scale, orientation: imgA.imageOrientation)
            .withTintColor(.white, renderingMode: .alwaysOriginal)
        self.image = imgB
        
        // add a round mask, inset by 1.0 so we don't see the anti-aliased edge
        let msk = CAShapeLayer()
        msk.path = UIBezierPath(ovalIn: bounds.insetBy(dx: 1.0, dy: 1.0)).cgPath
        layer.mask = msk
    }
    
}

Example view controller:

class FlagVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .systemBlue
        
        let greenView = UIView()

        greenView.backgroundColor = .systemGreen

        let flagView = FlagImageView(frame: .zero)
        
        flagView.backgroundColor = .red
        
        greenView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(greenView)

        flagView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(flagView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            greenView.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
            greenView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 100.0),
            greenView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            greenView.heightAnchor.constraint(equalToConstant: 80.0),

            flagView.topAnchor.constraint(equalTo: greenView.topAnchor, constant: 8.0),
            flagView.leadingAnchor.constraint(equalTo: greenView.leadingAnchor, constant: -12.0),
            flagView.widthAnchor.constraint(equalToConstant: 40.0),
            flagView.heightAnchor.constraint(equalTo: flagView.widthAnchor),
            
        ])
        
        greenView.layer.cornerRadius = 40.0
    }
    
}

Result:

enter image description here

There is a detailed explanation about SF Symbol usage here: https://stackoverflow.com/a/71743787/6257435

DonMag
  • 69,424
  • 5
  • 50
  • 86