Constraints обрезают наложенный Sublayer на UIButton

Сделал кнопку с закругленными краями, наложил сверху слой рамки с градиентом. Отдельно она рисуется как нужно, но после интеграции на основной экран и записи Constraints её верхний слой начинает обрезаться. (На скрине это видно справа и снизу кнопки) введите сюда описание изображения

Кнопка: mainSearchButtonView

Constraints задаются в initialize()

Sublayer накладывается при помощи расширения UIView.addGradientCornerBorders(...)

import UIKit

class ViewController: UIViewController {
    private let screenSize: CGRect = UIScreen.main.bounds
    private lazy var titleTopOffset: CGFloat = screenSize.height * 0.045
    private let cardWidthCoeff: Double = 0.9
    private let cardHeightCoeff: Double = 0.25
    private lazy var cardButtonWidthCoeff: Double = cardWidthCoeff * 0.3
    private lazy var cardButtonHeightCoeff: Double = cardHeightCoeff * 1.05

    private lazy var scrollView: UIScrollView = {
        let view = UIScrollView()
        
        view.translatesAutoresizingMaskIntoConstraints = false
        view.showsVerticalScrollIndicator = false
        view.contentInsetAdjustmentBehavior = .never
        
        return view
    }()
    private lazy var titleStackView: UIStackView = {
        let view = UIStackView()
        
        view.translatesAutoresizingMaskIntoConstraints = false
        view.axis = .horizontal
        view.spacing = 5
        
        return view
    }()
    private lazy var mainIconView: UIImageView = {
        let view = UIImageView()
        let image = UIImage(systemName: "location.viewfinder")?.resized(to: CGSize(width: screenSize.width * 0.11, height: screenSize.width * 0.11 * 7 / 8)) ?? UIImage()
        
        view.image = image
        view.tintColor = .black
        view.contentMode = .scaleAspectFit
        
        return view
    }()
    private lazy var mainLabelView: UILabel = {
        let view = UILabel()
        view.adjustsFontSizeToFitWidth = true
        view.minimumScaleFactor = 0.2
        view.numberOfLines = 1
        view.text = "label1"
        view.textAlignment = .center
        view.font = UIFont.systemFont(ofSize: screenSize.width * 0.095, weight: .bold)
        view.textColor = .black
        
        return view
    }()
    private let mainCardView: UIView = {
        let view = UIView()
        
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = UIColor(red: 241/255, green: 238/255, blue: 228/255, alpha: 1)
        view.layer.cornerRadius = 25
        view.layer.shadowColor = UIColor.gray.cgColor
        view.layer.shadowOpacity = 1
        view.layer.shadowOffset = .zero
        view.layer.shadowRadius = 5
        
        return view
    }()
    private lazy var mainSearchButtonView: UIButton = {
        let image = UIImage(systemName: "magnifyingglass")?.resized(to: CGSize(width: screenSize.width * 0.11, height: screenSize.width * 0.11 * 7 / 8))
        let cornerRadius: Double = 25.0
        
        let view = UIButton(type: .system)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.setTitle("", for: .normal)
        view.setImage(image, for: .normal)
        view.tintColor = .black
        view.frame.size = CGSize(width: screenSize.width * cardButtonWidthCoeff, height: screenSize.height * cardButtonHeightCoeff)
        view.backgroundColor = UIColor(red: 241/255, green: 238/255, blue: 228/255, alpha: 1)
        view.layer.cornerRadius = cornerRadius
        view.addGradientCornerBorders(lineWidth: 10, cornerRadius: cornerRadius, colors: [UIColor.blue.cgColor, UIColor.green.cgColor], startPoint: CGPoint.topRight, endPoint: CGPoint.bottomLeft)
        
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        initialize()
    }

    private func initialize() {
        overrideUserInterfaceStyle = .light
        view.backgroundColor = UIColor(red: 241/255, green: 238/255, blue: 228/255, alpha: 1)
        
        view.addSubview(scrollView)
        scrollView.addSubview(titleStackView)
        scrollView.addSubview(mainCardView)
        titleStackView.addArrangedSubview(mainIconView)
        titleStackView.addArrangedSubview(mainLabelView)
        mainCardView.addSubview(mainSearchButtonView)

        NSLayoutConstraint.activate([
            scrollView.topAnchor.constraint(equalTo: view.topAnchor),
            scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
            titleStackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: titleTopOffset),
            titleStackView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            
            mainCardView.topAnchor.constraint(greaterThanOrEqualTo: titleStackView.bottomAnchor, constant: screenSize.height * 0.05),
            mainCardView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor),
            mainCardView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: cardWidthCoeff),
            mainCardView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: cardHeightCoeff),

            mainSearchButtonView.centerYAnchor.constraint(equalTo: mainCardView.centerYAnchor),
            mainSearchButtonView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: cardButtonWidthCoeff),
            mainSearchButtonView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: cardButtonHeightCoeff),
            mainSearchButtonView.rightAnchor.constraint(equalTo: mainCardView.rightAnchor, constant: 10)
        ])
}

extension CGPoint {
    static let topLeft = CGPoint(x: 0, y: 0)
    static let topCenter = CGPoint(x: 0.5, y: 0)
    static let topRight = CGPoint(x: 1, y: 0)
    static let centerLeft = CGPoint(x: 0, y: 0.5)
    static let center = CGPoint(x: 0.5, y: 0.5)
    static let centerRight = CGPoint(x: 1, y: 0.5)
    static let bottomLeft = CGPoint(x: 0, y: 1.0)
    static let bottomCenter = CGPoint(x: 0.5, y: 1.0)
    static let bottomRight = CGPoint(x: 1, y: 1)
}

extension UIImage {
    func resized(to size: CGSize) -> UIImage {
        return UIGraphicsImageRenderer(size: size).image { _ in
            draw(in: CGRect(origin: .zero, size: size))
        }
    }
}

extension UIView {
    func addGradientCornerBorders(lineWidth: CGFloat, cornerRadius: Double, colors: [CGColor], startPoint: CGPoint = CGPoint.topCenter, endPoint: CGPoint = CGPoint.bottomCenter) {
        let gradient = CAGradientLayer()
        gradient.frame.size =  self.frame.size
        gradient.colors = colors
        gradient.startPoint = startPoint
        gradient.endPoint = endPoint
        
        let shape = CAShapeLayer()
        shape.lineWidth = lineWidth
        shape.path = UIBezierPath(roundedRect: self.bounds, cornerRadius: cornerRadius).cgPath
        shape.strokeColor = UIColor.black.cgColor
        shape.fillColor = UIColor.clear.cgColor
        gradient.mask = shape
        
        self.clipsToBounds = true
        self.layer.addSublayer(gradient)
    }
}

Ответы (1 шт):

Автор решения: Oleg Soloviev

Посмотрел на SE. Тут, возможно, артефакты из-за не круглых значений, которые используются для расчета углов. Попробуйте поэкспериментировать с коэффициентами, например так:

private lazy var cardButtonWidthCoeff: Double = cardWidthCoeff * 0.32
private lazy var cardButtonHeightCoeff: Double = cardHeightCoeff * 1.06

введите сюда описание изображения

→ Ссылка