Search This Blog

Friday, October 6, 2017

iOS Live Rendering with @IBInspectable and @IBDesignable

How Live Rendering works


Live Rendering allows to setup custom properties for interface elements and see results immediately in interface builder.

Live Rendering allows to setup custom properties for interface elements and see results immediatly in interface builder. Let's create custom view with possibility to setup border color, border width and corner radius with Interface Builder. For this purpose let's create custom class CustomView and inherit it from UIView.
For Live Rendering annotate class with @IBDesignable and for setup properties add @IBInspectable annotation.
import Foundation
import UIKit

@IBDesignable
class CustomView: UIView {
    
    @IBInspectable
    var borderColor: UIColor? {
        didSet {
            self.layer.borderColor = borderColor?.cgColor
        }
    }
    
    @IBInspectable
    var borderWidth: CGFloat = 0 {
        didSet {
            self.layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat = 0 {
        didSet {
            self.layer.cornerRadius = cornerRadius
            self.layer.masksToBounds = (cornerRadius > 0)
        }
    }
}

Custom View with custom properties



Class of UIView must be set to CustomView





We can define maskToBounds property depends on cornerRadius.
@IBInspectable
var cornerRadius: CGFloat = 0 {
    didSet {
        self.layer.cornerRadius = cornerRadius
        self.layer.masksToBounds = (cornerRadius > 0)
    }
}

Or we can move it to separate @Inspectable property.
@IBInspectable
var maskToBounds: Bool = false {
    didSet {
        self.layer.masksToBounds = maskToBounds
    }
}

import Foundation
import UIKit

@IBDesignable
class CustomView: UIView {
    
    @IBInspectable
    var borderColor: UIColor? {
        didSet {
            self.layer.borderColor = borderColor?.cgColor
        }
    }
    
    @IBInspectable
    var borderWidth: CGFloat = 0 {
        didSet {
            self.layer.borderWidth = borderWidth
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat = 0 {
        didSet {
            self.layer.cornerRadius = cornerRadius
        }
    }
    
    @IBInspectable
    var maskToBounds: Bool = false {
        didSet {
            self.layer.masksToBounds = maskToBounds
        }
    }
}


When maskToBounds property is set to false then inner view is overlap superview.


When maskToBounds property set to true.




But we can make this properties available for all views in project. So let's create Extension fot UIView and move to it all inspectable properties.
import Foundation
import UIKit

extension UIView {
    
    @IBInspectable
    var borderColor: UIColor? {
        set {
            self.layer.borderColor = newValue?.cgColor
        }
        get {
            if let color = self.layer.borderColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
    }
    
    @IBInspectable
    var borderWidth: CGFloat {
        set {
            self.layer.borderWidth = newValue
        }
        get {
            return self.layer.borderWidth
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat {
        set {
            self.layer.cornerRadius = newValue
        }
        get {
            return self.layer.cornerRadius
        }
    }
    
    @IBInspectable
    var maskToBounds: Bool {
        set {
            self.layer.masksToBounds = newValue
        }
        get {
            return self.layer.masksToBounds
        }
    }
    
}


And remove all properties from CustomView class. But left @IBDesignable annotation for Live Rendering.
import Foundation
import UIKit

@IBDesignable
class CustomView: UIView {
    
}


Let's go forward and add properties for displaying shadow.
import Foundation
import UIKit

extension UIView {
    
    @IBInspectable
    var borderColor: UIColor? {
        set {
            self.layer.borderColor = newValue?.cgColor
        }
        get {
            if let color = self.layer.borderColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
    }
    
    @IBInspectable
    var borderWidth: CGFloat {
        set {
            self.layer.borderWidth = newValue
        }
        get {
            return self.layer.borderWidth
        }
    }
    
    @IBInspectable
    var cornerRadius: CGFloat {
        set {
            self.layer.cornerRadius = newValue
        }
        get {
            return self.layer.cornerRadius
        }
    }
    
    @IBInspectable
    var maskToBounds: Bool {
        set {
            self.layer.masksToBounds = newValue
        }
        get {
            return self.layer.masksToBounds
        }
    }
    
    @IBInspectable
    var shadowColor: UIColor? {
        set {
            self.layer.shadowColor = newValue?.cgColor
        }
        get {
            if let color = self.layer.shadowColor {
                return UIColor(cgColor: color)
            }
            return nil
        }
    }
    
    @IBInspectable
    var shadowRadius: CGFloat {
        set {
            self.layer.shadowRadius = newValue
        }
        get {
            return self.layer.shadowRadius
        }
    }
    
    @IBInspectable
    var shadowOffset: CGSize {
        set {
            self.layer.shadowOffset = newValue
        }
        get {
            return self.layer.shadowOffset
        }
    }
    
    @IBInspectable
    var shadowOpacity: Float {
        set {
            self.layer.shadowOpacity = newValue
        }
        get {
            return self.layer.shadowOpacity
        }
    }
    
}

UIView with shadow. Now we can set shadow from Interface Builder.





If we want to create custom inspectable enumerated property.

import Foundation
import UIKit

enum ViewStyle: String {
    
    case Rounded = "rounded"
    case Shadowed = "shadowed"
    
    var maskToBounds: Bool {
        switch self {
        case .Rounded: return true
        case .Shadowed: return false
        }
    }
    
    var cornerRadius: CGFloat {
        return 6
    }
    
    var borderWidth: CGFloat {
        return 2
    }
    
    var borderColor: UIColor {
        return UIColor.black
    }
    
    var shadowColor: UIColor{
        return UIColor.black
    }
    
    var shadowRadius: CGFloat {
        return 5.0
    }
    
    var shadowOffset: CGSize {
        return CGSize(width: 5.0, height: 5.0)
    }
    
    var shadowOpacity: Float {
        return 1.0
    }
}


Add this property to UIView Extension.
import Foundation
import UIKit

extension UIView {
    
 .....
    
    @IBInspectable
    var viewStyle: String? {
        set {
            self.configureWithStyle(viewStyle: newValue)
        }
        get {
            return nil
        }
    }
    
    private func configureWithStyle(viewStyle: String?) {
        if let viewStyleStr = viewStyle {
            if let styleEnum = ViewStyle(rawValue: viewStyleStr) {
                self.borderColor = styleEnum.borderColor
                self.borderWidth = styleEnum.borderWidth
                self.cornerRadius = styleEnum.cornerRadius
                self.maskToBounds = styleEnum.maskToBounds
                self.shadowOpacity = styleEnum.shadowOpacity
                self.shadowOffset = styleEnum.shadowOffset
                self.shadowRadius = styleEnum.shadowRadius
                self.shadowColor = styleEnum.shadowColor
            }
        }
    }
    
}


UIView with style "rounded"



UIView with style "shadowed"



Running on the device



GitHub Link

No comments:

Post a Comment