首页 > 解决方案 > 是否可以以编程方式更新情节提要

问题描述

我用 UIView 子类创建了一个名为 Container 的类。

在 UIViewController 中,我使用了 Container 类的子类,我称之为 LoginContainer。

Container 类的代码是

@IBDesignable open class Container: UIView {
    weak var controller: UIViewController!
    var constraintsOK: Bool = false
    
    @IBInspectable open var borderColor: UIColor = UIColor.clear {
        didSet {
            layer.borderColor = borderColor.cgColor
            setNeedsLayout()
        }
    }
    
    @IBInspectable open var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            layer.masksToBounds = newValue > 0
            setNeedsLayout()
        }
    }

    @IBInspectable open var borderWidth: CGFloat = 1.0 {
        didSet {
            layer.borderWidth = borderWidth / UIScreen.main.scale
            setNeedsLayout()
        }
    }
    
    @IBInspectable open var shadow: Bool = false {
        didSet {
            if shadow {
                addShadow()
                setNeedsLayout()
            }
        }
    }
    
    // MARK: - Initializers

    // IBDesignables require both of these inits, otherwise we'll get an error: IBDesignable View Rendering times out.
    // http://stackoverflow.com/questions/26772729/ibdesignable-view-rendering-times-out
    
    /// Pour les initializers, ne pas appeler customInit dans InterfaceBuilder
    ///  car déjà appelé dans PrepareforInterfaceBuilder
    override public init(frame: CGRect) {
        super.init(frame: frame)
        #if !TARGET_INTERFACE_BUILDER
        customInit()
        #endif
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        #if !TARGET_INTERFACE_BUILDER
        customInit()
        #endif
    }
    
    // MARK: - Build control

    override public func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        customInit()
    }
    
    /// Overriden method must call super.customInit().
    open func customInit() {
        addBorder()
        /// L'ombre, s'il y en a est ajoutée dans layoutSubviews
    }
    
    /// Must be overriden when subclassed
    open func setConstraints() {}
    
    /// Adds a border to the control.
    open func addBorder() {
        layer.borderColor = borderColor.cgColor
        layer.borderWidth = borderWidth / UIScreen.main.scale

        // http://stackoverflow.com/questions/4735623/uilabel-layer-cornerradius-negatively-impacting-performance
        if layer.shouldRasterize == false {
            layer.masksToBounds = true
            layer.rasterizationScale = UIScreen.main.scale
            layer.shouldRasterize = true
        }
    }
    
    open func addShadow() {
        /// Attention, la couleur de fonds ne doit pas être à transparent, sinon on voit toute l'ombre en transparence
        clipsToBounds = false
        let shadowRect = CGRect(x: 10, y: 20, width: bounds.width-10, height: bounds.height-15)
        layer.shadowColor = CGColor(red: 14/255, green: 47/255, blue: 133/255, alpha: 1)
        layer.shadowOpacity = 1
        layer.shadowPath = UIBezierPath(rect: shadowRect).cgPath
        layer.shadowRadius = layer.cornerRadius
        
        // https://www.hackingwithswift.com/example-code/uikit/how-to-add-a-shadow-to-a-uiview
        layer.shadowPath = UIBezierPath(rect: shadowRect).cgPath
        if layer.shouldRasterize == false {
            layer.masksToBounds = false
            layer.shouldRasterize = true
            layer.rasterizationScale = UIScreen.main.scale
        }
    }
    
    // MARK: - LifeCycle
    
    override public func layoutSubviews() {
        super.layoutSubviews()
        if !constraintsOK {
            setConstraints()
        }
    }
}

LoginContainer 子类的代码是

import UIKit

@IBDesignable class LoginContainer: Container {
    
    override public func setConstraints() {
        translatesAutoresizingMaskIntoConstraints = false
        #if TARGET_INTERFACE_BUILDER
        if let view = findViewController()?.view {
            leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
            topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
            widthAnchor.constraint(equalToConstant: 100).isActive = true
            heightAnchor.constraint(equalToConstant: 100).isActive = true
        }
        #endif
        if let view = controller?.view {

            leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10).isActive = true
            topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
            widthAnchor.constraint(equalToConstant: 100).isActive = true
            heightAnchor.constraint(equalToConstant: 100).isActive = true
        }
        constraintsOK = true
    }

}

最后 UIViewController 的代码是

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var container: LoginContainer!
    
    override func viewDidLoad() {
        container.controller = self
        super.viewDidLoad()
    }


}

extension UIView {
    public func findViewController() -> UIViewController? {
        let nextResponder = self.next
        if nextResponder is UIViewController {
            return nextResponder as? UIViewController
        } else if nextResponder is UIView {
            return (nextResponder as! UIView).findViewController()
        } else {
            return nil
        }
    }
}

当我查看情节提要时,一切都正确,LoginContainer 的约束已正确更新

未选择视图 LoginContainer

但是在情节提要中,我选择了 LoginContainer 视图,这就是我所看到的

视图 LoginContainer 被选中

LoginContainer 选择的链接不反映实际情况

如果 contentView 中包含多个容器视图,这可能会成为一个真正的问题(例如考虑键盘视图,其中每个键都由一个 Container 类表示)

所以我的问题是:是否可以通过程序更新情节提要,或者至少让 IB 在选择视图时显示正确的位置?

标签: iosswiftstoryboard

解决方案


推荐阅读