From 991c66318435bce60252d606afcdf56eea258933 Mon Sep 17 00:00:00 2001 From: TheFonz2017 Date: Tue, 22 Aug 2017 11:53:06 +0200 Subject: [PATCH] Extended NibDesignable Version --- NibDesignable.swift | 185 +++++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 46 deletions(-) diff --git a/NibDesignable.swift b/NibDesignable.swift index 756e617..1f0542f 100644 --- a/NibDesignable.swift +++ b/NibDesignable.swift @@ -23,6 +23,16 @@ import UIKit + +/** + Protocol for `NibDesignable` pattern (related to `@IBDesignable`) for loading xib-based views for `UIView` subclasses in an `@IBDesignable`-compatible way. Views which are `NibDesignable` may typically be rendered in Interface Builder. + + ## See also + + [NibDesignable](https://github.com/mbogh/NibDesignable) project on github.com (MIT License) + + - authors: [@mbogh](https://github.com/mbogh), *et al*. + */ public protocol NibDesignableProtocol: NSObjectProtocol { /** Identifies the view that will be the superview of the contents loaded from @@ -31,34 +41,46 @@ public protocol NibDesignableProtocol: NSObjectProtocol { - returns: Superview for Nib contents. */ var nibContainerView: UIView { get } - // MARK: - Nib loading - - /** - Called to load the nib in setupNib(). - - - returns: UIView instance loaded from a nib file. - */ - func loadNib() -> UIView + /** Called in the default implementation of loadNib(). Default is class name. - returns: Name of a single view nib file. */ - func nibName() -> String + var nibName: String { get } + + /** + Called in the default implementation of loadNib(). Default is the bundle of the custom view class. + + - returns: the bundle of the NibDesigable to look for the Nib for. + */ + var bundle: Bundle { get } + + /** + Specifies which class should be responsible for loading the nib of this NibDesignable. + Defaults to the class that implements the NibDesignable protocol. + However, if subclasses of NibDesignable views (i.e. subclasses of subclasses of NibDesignable) don't want to + handle their own Nib but rely on that of their superclass, they should override this variable and return the + super class. This will make sure the superclass handles nib loading and will result in the same nib being used + for the sub class as was used by the superclass. + + - returns: the class that handles the nib loading. This class needs to be implementing NibDesignableProtocol. + */ + var nibLoadingClass: AnyClass { get } } -extension NibDesignableProtocol { - // MARK: - Nib loading - +extension NibDesignableProtocol where Self: UIView { + /** Called to load the nib in setupNib(). - returns: UIView instance loaded from a nib file. */ - public func loadNib() -> UIView { - let bundle = Bundle(for: type(of: self)) - let nib = UINib(nibName: self.nibName(), bundle: bundle) - return nib.instantiate(withOwner: self, options: nil)[0] as! UIView // swiftlint:disable:this force_cast + internal func loadNib() -> UIView { + let bundle = self.bundle + let nib = UINib(nibName: self.nibName, bundle: bundle) + let m_view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView // swiftlint:disable:this force_cast + return m_view } // MARK: - Nib loading @@ -66,130 +88,201 @@ extension NibDesignableProtocol { /** Called in init(frame:) and init(aDecoder:) to load the nib and add it as a subview. */ - fileprivate func setupNib() { + internal func setupNib() { let view = self.loadNib() self.nibContainerView.addSubview(view) view.translatesAutoresizingMaskIntoConstraints = false let bindings = ["view": view] self.nibContainerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", options:[], metrics:nil, views: bindings)) self.nibContainerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", options:[], metrics:nil, views: bindings)) + self.nibContainerView.preservesSuperviewLayoutMargins = true + + if #available(iOS 11.0, *) { + // do nothing, because it works ok + } else { + // add the preservesSuperviewLayoutMargins workaround + // will be executed for anything < 11.0 + view.preservesSuperviewLayoutMargins = true + } } -} -extension UIView { + /// The nib container view of nib designable view. + /// Defaults to the view itself. public var nibContainerView: UIView { return self } - /** - Called in the default implementation of loadNib(). Default is class name. - - returns: Name of a single view nib file. - */ - open func nibName() -> String { - return type(of: self).description().components(separatedBy: ".").last! + /// The name of the nib that the contents of this view is loaded from. + public var nibName: String { + return self.nibLoadingClass.description().components(separatedBy: ".").last! + } + /// The name of the bundle to look for the nib in. Defaults to the bundle + /// that contains the nibLoadingClass. + public var bundle: Bundle { + return Bundle(for: self.nibLoadingClass) + } + + /// The class that is responsible for loading the nib. Defaults to the UIView's (sub)class itself. + public var nibLoadingClass: AnyClass { + return type(of: self) } } +/** + `NibDesignableProtocol`-compliant implementation of `UIView` + + ## See also + `NibDesignableProtocol` + */ @IBDesignable open class NibDesignable: UIView, NibDesignableProtocol { // MARK: - Initializer + /// :nodoc: override public init(frame: CGRect) { super.init(frame: frame) self.setupNib() } // MARK: - NSCoding + /// :nodoc: required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupNib() } } +/** + `NibDesignableProtocol`-compliant implementation of `UITableViewCell` + + ## See also + `NibDesignableProtocol` + */ @IBDesignable open class NibDesignableTableViewCell: UITableViewCell, NibDesignableProtocol { - public override var nibContainerView: UIView { + + /// The nib container view of this TableView cell. + open var nibContainerView: UIView { return self.contentView } // MARK: - Initializer + /// :nodoc: override public init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) + self.preservesSuperviewLayoutMargins = true + self.setupNib() + } + + // MARK: - NSCoding + /// :nodoc: + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.preservesSuperviewLayoutMargins = true self.setupNib() } +} +/** + `NibDesignableProtocol`-compliant implementation of `UIControl` + + ## See also + `NibDesignableProtocol` + */ +@IBDesignable +open class NibDesignableControl: UIControl, NibDesignableProtocol { + + // MARK: - Initializer + /// :nodoc: + override public init(frame: CGRect) { + super.init(frame: frame) + self.setupNib() + } + // MARK: - NSCoding + /// :nodoc: required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupNib() } } +/** + `NibDesignableProtocol`-compliant implementation of `UITableViewHeaderFooterView` + + ## See also + `NibDesignableProtocol` + */ @IBDesignable open class NibDesignableTableViewHeaderFooterView: UITableViewHeaderFooterView, NibDesignableProtocol { - public override var nibContainerView: UIView { + /// The nib container view of this TableView header footer view. + open var nibContainerView: UIView { return self.contentView } - // MARK: - Initializer + // MARK: - Initializer + /// :nodoc: override public init(reuseIdentifier: String?) { super.init(reuseIdentifier: reuseIdentifier) self.setupNib() } - // MARK: - NSCoding + // MARK: - NSCoding + /// :nodoc: required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupNib() } } -@IBDesignable -open class NibDesignableControl: UIControl, NibDesignableProtocol { - - // MARK: - Initializer - override public init(frame: CGRect) { - super.init(frame: frame) - self.setupNib() - } - - // MARK: - NSCoding - required public init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - self.setupNib() - } -} - +/** + `NibDesignableProtocol`-compliant implementation of `UICollectionReusableView` + + ## See also + `NibDesignableProtocol` + */ @IBDesignable open class NibDesignableCollectionReusableView: UICollectionReusableView, NibDesignableProtocol { // MARK: - Initializer + /// :nodoc: override public init(frame: CGRect) { super.init(frame: frame) self.setupNib() } // MARK: - NSCoding + /// :nodoc: required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupNib() } } +/** + `NibDesignableProtocol`-compliant implementation of `UICollectionViewCell` + + ## See also + `NibDesignableProtocol` + */ @IBDesignable open class NibDesignableCollectionViewCell: UICollectionViewCell, NibDesignableProtocol { - public override var nibContainerView: UIView { + + /// The nib container view of this collection view cell. + open var nibContainerView: UIView { return self.contentView } // MARK: - Initializer + /// :nodoc: override public init(frame: CGRect) { super.init(frame: frame) self.setupNib() } // MARK: - NSCoding + /// :nodoc: required public init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setupNib()