James Faulkner
James Faulkner

Reputation: 107

Neumorphism design using Swift storyboards

I have seen quite a few tutorials online referring to the use of neumorphism design. However it appears to be only limited to SwiftUI.

Therefore it leads me to two questions:

  1. Is this design style possible using regular storyboards (without SwiftUI) on an existing UIView?

  2. Failing question 1, is it possible to inject a SwiftUI element, if the majority of the project is configured using storyboards.

Upvotes: 1

Views: 845

Answers (1)

vaishali
vaishali

Reputation: 17

class NeumorphicButton: UIButton {
    
    private let darkShadow = CALayer()
    private let lightShadow = CALayer()
    
    private let radius: CGFloat = 20
    private let shadowRadiuss: CGFloat = 5
    
    lazy var innerShadowLayer: CAShapeLayer = {
        let shadowLayer = CAShapeLayer()
        shadowLayer.shadowColor = UIColor.black.cgColor
        shadowLayer.shadowOffset = CGSize(width: 0.0, height: 0.0)
        shadowLayer.shadowOpacity = 0.1
        shadowLayer.shadowRadius = 14
        shadowLayer.fillRule = .evenOdd
        return shadowLayer
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupButton()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupButton()
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        updateShadows()
    }
    
    private func setupButton() {
        clipsToBounds = false // Important for shadows
        layer.cornerRadius = radius
        backgroundColor = UIColor.systemGray6 // Match with the superview's background

        setupShadows()
    }
    
    private func setupShadows() {
        // Dark shadow (bottom-right)
        darkShadow.removeFromSuperlayer()
        lightShadow.removeFromSuperlayer()
        
        darkShadow.shadowColor = UIColor(white: 0.8, alpha: 1.0).cgColor
        darkShadow.shadowOffset = CGSize(width: shadowRadiuss, height: shadowRadiuss)
        darkShadow.shadowOpacity = 1
        darkShadow.shadowRadius = shadowRadiuss

        // Light shadow (top-left)
        lightShadow.shadowColor = UIColor.white.cgColor
        lightShadow.shadowOffset = CGSize(width: -shadowRadiuss, height: -shadowRadiuss)
        lightShadow.shadowOpacity = 1
        lightShadow.shadowRadius = shadowRadiuss
        
        layer.insertSublayer(darkShadow, at: 0)
        layer.insertSublayer(lightShadow, at: 0)
    }
    
    private func updateShadows() {
        darkShadow.frame = bounds
        lightShadow.frame = bounds
        
        darkShadow.cornerRadius = radius
        lightShadow.cornerRadius = radius
        
        darkShadow.backgroundColor = backgroundColor?.cgColor
        lightShadow.backgroundColor = backgroundColor?.cgColor
    }
    
    func setUpIsPressed() {
        self.layer.masksToBounds = true
        self.layer.addSublayer(self.innerShadowLayer)
        
        let shadowPath = CGMutablePath()
        let inset = -self.innerShadowLayer.shadowRadius * 2.0
        shadowPath.addRect(self.bounds.insetBy(dx: inset, dy: inset))
        shadowPath.addRect(self.bounds)
        self.innerShadowLayer.path = shadowPath
    }
    
    func setUpPressedReleased() {
        clipsToBounds = false
        backgroundColor = UIColor.systemGray6
        innerShadowLayer.removeFromSuperlayer()
        setupShadows()
    }
    
    override var isHighlighted: Bool {
        didSet {
            if isHighlighted {
                setUpIsPressed()
            } else {
                setUpPressedReleased()
            }
        }
    }
}

when button pressed it will add inner shadow to make it look like pressed.

Upvotes: 0

Related Questions