Home > Blockchain >  Constraint having issues in swift
Constraint having issues in swift

Time:12-17

I am having issue with constraints. I am using UIBezierPath and auto layout. I have done almost everything given here and still the issue is not rectified and that is why I am posting this question. I am using the following function for rounding certain corner of my view (which I have made using storyboard)

func addShadowAndCorner(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
    
    let shadowLayer = CAShapeLayer()
    let size = CGSize(width: cornerRadius, height: cornerRadius)
    let cgPath = UIBezierPath(roundedRect: self.curvedView.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
    shadowLayer.path = cgPath //2
    shadowLayer.fillColor = fillColor.cgColor //3
    shadowLayer.shadowColor = shadowColor.cgColor //4
    shadowLayer.shadowPath = cgPath
    shadowLayer.shadowOffset = offSet //5
    shadowLayer.shadowOpacity = opacity
    shadowLayer.shadowRadius = shadowRadius
    self.curvedView.layer.addSublayer(shadowLayer)
}

Then I am calling this function as below

func configureView() {
    self.view.backgroundColor = UIColor(named: "appBackgroundColor")
    curvedView.backgroundColor = .clear
    self.addShadowAndCorner(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)
}

I have called the above function inside "override func viewDidLayoutSubviews()". But I am not getting expected behaviours for constraints on different phone sizes

The code works well when running on iPhone 11 simulator, the screenshot is shared below.

enter image description here

But when running the same code on iPhone SE simulator it is not working as expected. The screenshot is shared below for iPhone SE

enter image description here

The constraints for curvedView in storyboard is.

enter image description here

Please help me here. Thanks in advance

CodePudding user response:

You should apply the shadow code in didLayoutSubviews. At that point your views know their frame sizes. Make sure you call super too.

Be careful not to re-add the shadow sublayer every time by just calling your addShadowAndCorner function from didLayoutSubviews.

CodePudding user response:

You'll have the most reliable (and flexible) results by creating a custom UIView subclass.

Here's a quick example:

class MyCustomView: UIView {

    // properties with default values
    var shadowColor: UIColor = .darkGray
    var offset: CGSize = .zero
    var opacity: Float = 1.0
    var shadowRadius: CGFloat = 0.0
    var cornerRadius: CGFloat = 0.0
    var corners: UIRectCorner = []
    var fillColor: UIColor = .white
    
    let shadowLayer = CAShapeLayer()

    convenience init(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
        self.init(frame: .zero)
        self.shadowColor = shadowColor
        self.offset = offSet
        self.opacity = opacity
        self.shadowRadius = shadowRadius
        self.cornerRadius = cornerRadius
        self.corners = corners
        self.fillColor = fillColor
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        layer.addSublayer(shadowLayer)
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        
        let size = CGSize(width: cornerRadius, height: cornerRadius)
        let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
        shadowLayer.path = cgPath //2
        shadowLayer.fillColor = fillColor.cgColor //3
        shadowLayer.shadowColor = shadowColor.cgColor //4
        shadowLayer.shadowPath = cgPath
        shadowLayer.shadowOffset = offset //5
        shadowLayer.shadowOpacity = opacity
        shadowLayer.shadowRadius = shadowRadius
    }

    func configureView(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
        self.shadowColor = shadowColor
        self.offset = offSet
        self.opacity = opacity
        self.shadowRadius = shadowRadius
        self.cornerRadius = cornerRadius
        self.corners = corners
        self.fillColor = fillColor
        setNeedsLayout()
    }

}

You can then add a UIView in Storyboard, assign its Custom Class to MyCustomView, connect it to an @IBOutlet, and then in viewDidLoad():

class MyTestVC: UIViewController {
    
    @IBOutlet var curvedView: MyCustomView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        curvedView.backgroundColor = .clear
        curvedView.configureView(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)

    }
}

Now, it will auto-update itself when its frame changes... such as on different devices or on device rotation:

enter image description here

enter image description here

enter image description here

You can create it via code like this:

let curvedView = MyCustomView(shadowColor: .darkGray, offSet: CGSize.init(width: 3.0, height: 3.0), opacity: 0.6, shadowRadius: 8, cornerRadius: 80, corners: [.topRight, .bottomLeft], fillColor: .white)

and, with very little effort, you can make it @IBDesignable and configure your properties as @IBInspectable ... then you can visually see the result when you lay it out in Storyboard.

The only tricky property is the Corners, because there is no direct @IBInspectable option for UIRectCorner ... so we can use Bool properties for each corner:

@IBDesignable
class MyCustomView: UIView {
    
    // properties with default values
    @IBInspectable var shadowColor: UIColor = .darkGray
    @IBInspectable var offset: CGSize = .zero
    @IBInspectable var opacity: Float = 1.0
    @IBInspectable var shadowRadius: CGFloat = 0.0
    @IBInspectable var cornerRadius: CGFloat = 0.0
    @IBInspectable var topLeft: Bool = false
    @IBInspectable var topRight: Bool = false
    @IBInspectable var bottomLeft: Bool = false
    @IBInspectable var bottomRight: Bool = false
    @IBInspectable var fillColor: UIColor = .white
    
    private let shadowLayer = CAShapeLayer()
    
    convenience init(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
        self.init(frame: .zero)
        self.shadowColor = shadowColor
        self.offset = offSet
        self.opacity = opacity
        self.shadowRadius = shadowRadius
        self.cornerRadius = cornerRadius
        self.fillColor = fillColor
        
        self.topLeft = corners.contains(.topLeft)
        self.topRight = corners.contains(.topRight)
        self.bottomLeft = corners.contains(.bottomLeft)
        self.bottomRight = corners.contains(.bottomRight)
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    private func commonInit() {
        layer.addSublayer(shadowLayer)
    }
    override func layoutSubviews() {
        super.layoutSubviews()

        backgroundColor = .clear
        
        var corners: UIRectCorner = UIRectCorner()
        
        if self.topLeft { corners.insert(.topLeft) }
        if self.topRight { corners.insert(.topRight) }
        if self.bottomLeft { corners.insert(.bottomLeft) }
        if self.bottomRight { corners.insert(.bottomRight) }

        let size = CGSize(width: cornerRadius, height: cornerRadius)
        let cgPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: size).cgPath //1
        shadowLayer.path = cgPath
        shadowLayer.fillColor = self.fillColor.cgColor
        shadowLayer.shadowColor = self.shadowColor.cgColor
        shadowLayer.shadowPath = cgPath
        shadowLayer.shadowOffset = self.offset
        shadowLayer.shadowOpacity = self.opacity
        shadowLayer.shadowRadius = self.shadowRadius
    }
    
    public func configureView(shadowColor: UIColor, offSet: CGSize, opacity: Float, shadowRadius: CGFloat, cornerRadius: CGFloat, corners: UIRectCorner, fillColor: UIColor = .white) {
        self.shadowColor = shadowColor
        self.offset = offSet
        self.opacity = opacity
        self.shadowRadius = shadowRadius
        self.cornerRadius = cornerRadius
        self.fillColor = fillColor
        
        self.topLeft = corners.contains(.topLeft)
        self.topRight = corners.contains(.topRight)
        self.bottomLeft = corners.contains(.bottomLeft)
        self.bottomRight = corners.contains(.bottomRight)

        setNeedsLayout()
    }
    
}

Now, we get this in Storyboard:

enter image description here

enter image description here

  • Related