Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# CHANGELOG

## Unreleased

* Removing a parameter from `loadFromNib(owner:)` method in `NibOwnerLoadable` due to a possibility of calling `init()` or
`init(frame:)` initializers which might led to a crash if `init(frame:)` was not implemented in a custom UIView subclass
(because it is not a required initializer).
Additionally, changing `loadFromNib(owner:)` to an instance method as for
classes conforming to NibOwnerLoadable, the instance of a class is already instantiated (i.e. when loading from other
XIBs). However, it is still possible to load such a view programatically.

⚠️ **BREAKING CHANGES** ⚠️ The following method has a new signature:
- `static func loadFromNib(owner:)` is now `func loadNibContent()`
[@Skoti](https://github.com/Skoti)
[#40](https://github.com/AliSoftware/Reusable/pull/40)

* Fixing incorrect protocol naming in documentation comments for CollectionHeaderView and MyXIBIndexSquaceCell
[@Skoti](https://github.com/Skoti)

* Fixing table view controller scene to display cells above UITabBar
[@Skoti](https://github.com/Skoti)

Copy link
Owner

@AliSoftware AliSoftware Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rework your entry so that it follows the same format as the other entries.
In particular, use a period + 2 spaces at the end of the lines describing the changes so that its properly formatted when the markdown is rendered (as a "new line in the same paragraph")

May I suggest something along those lines instead?

## Unreleased

### Breaking changes

* `static func loadFromNib(owner:)` of `NibOwnerLoadable` has been replaced by instance method `func loadNibContent()`.  
  This is more consistent and also avoids possible crashes when used with `UIView` subclasses not implementing non-required initializers `init()`/`init(frame:)`.  
  [@Skoti](https://github.com/Skoti)
  [#40](https://github.com/AliSoftware/Reusable/pull/40)

### Enhencements

* Fixing documentation typos for `CollectionHeaderView` and `MyXIBIndexSquaceCell`.  
  [@Skoti](https://github.com/Skoti)
* Fixing table view controller scene in Example project, to display cells above `UITabBar`.  
  [@Skoti](https://github.com/Skoti)

Copy link
Contributor Author

@Skoti Skoti Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, although I'm gonna use "Enhancements"

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops 😄 👍

## 3.0.1

* Fix `instantiate()` implementation on `StoryboardSceneBased` ViewControllers.
Expand Down
5 changes: 3 additions & 2 deletions Example/ReusableDemo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="7fF-Ae-xTS">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="7fF-Ae-xTS">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
Expand Down Expand Up @@ -90,6 +90,7 @@
<outlet property="delegate" destination="Spa-tB-Bt1" id="iqn-sB-Tfo"/>
</connections>
</tableView>
<extendedEdge key="edgesForExtendedLayout" top="YES"/>
<tabBarItem key="tabBarItem" title="TableView" id="C3o-Lt-Bjj"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XEN-tN-mKX" userLabel="First Responder" sceneMemberID="firstResponder"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Reusable
* It is also reusable and has a `reuseIdentifier` (as it's a CollectionViewCell
* and it uses the CollectionView recycling mechanism) => it is `Reusable`
*
* That's why it's annotated with the `NibOwnerLoadable` protocol,
* That's why it's annotated with the `NibReusable` protocol,
* Which in fact is just a convenience typealias that combines
* `NibLoadable` & `Reusable` protocols.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Reusable
* It is also reusable and has a `reuseIdentifier` (as it's a CollectionViewCell
* and it uses the CollectionView recycling mechanism) => it is `Reusable`
*
* That's why it's annotated with the `NibOwnerLoadable` protocol,
* That's why it's annotated with the `NibReusable` protocol,
* Which in fact is just a typealias that combines
* `NibLoadable` & `Reusable` protocols.
*/
Expand Down
5 changes: 1 addition & 4 deletions Example/ReusableDemo/CustomViews/MyCustomWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ class MyCustomWidget: UIView, NibOwnerLoadable {

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
MyCustomWidget.loadFromNib(owner: self)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.loadNibContent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Reusable
*
* That's why it's annotated with the `NibOwnerLoadable` protocol.
*/
final class MyHeaderTableView: UIView, NibOwnerLoadable, FrameInitializable {
final class MyHeaderTableView: UIView, NibOwnerLoadable {

@IBOutlet private weak var titleLabel: UILabel!
static let height: CGFloat = 55
Expand Down
3 changes: 2 additions & 1 deletion Example/ReusableDemo/TableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ final class TableViewController: UITableViewController {
}

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let view = MyHeaderTableView.loadFromNib()
let view = MyHeaderTableView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: self.tableView(tableView, heightForHeaderInSection: section)))
view.loadNibContent()
Copy link
Owner

@AliSoftware AliSoftware Feb 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe to better demonstrate the possibility of loading NibOwnerLoadable views from code, it would be better in this case to override the init(frame: ) of MyHeaderTableView so it calls self.loadNibContent() in that subclass' initializer? (instead of having to think about calling loadNibContent() on every new MyHeaderTableView instance we create like here?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, I'm gonna use it

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be the occasion to:

  • Break that line 30 in 2 lines (let frame = CGRect(…) + let view = MyHeaderTableView(frame: frame)
  • Add a comment just above this call to init to remind the reader of the example project that "this calls the overriden init of MyHeaderTableView which loads its content from its Nib automatically via loadNibContent()" or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like that?
"This calls the overriden init of MyHeaderTableView which loads its content from the default nib (see NibOwnerLoadable extension) automatically via loadNibContent() instance method."

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, maybe just shorten a bit like this?

// See the overridden `MyHeaderTableView.init(frame:)` initializer, which
// automatically loads the view content from its nib using loadNibContent()

view.fillForSection(section)
return view
}
Expand Down
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,17 +297,14 @@ final class MyCustomWidget: UIView, NibOwnerLoadable {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
MyCustomWidget.loadFromNib(owner: self)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.loadNibContent()
}
}
```

Overriding `init?(coder:)` allows your `MyCustomWidget` custom view to load its content from the associated XIB `MyCustomWidget.xib` and add it as subviews of itself.

_💡 Note: overriding `init(frame:)`, even just to call `super.init(frame: frame)` might seems pointless, but seems necessary in some cases due to a strange issue with Swift and dynamic dispatch not being able to detect and call the superclass implementation all by itself: I've sometimes seen crashes when not implementing it explicitly, so better safe than sorry._
_💡 Note: possible to override `init(frame:)`, in order to be able to create an instance of that view programatically and call `loadNibContent()` to fill with views if needed.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Note: it is also possible to …"


## 3b. Instantiating a `NibLoadable` view

Expand All @@ -322,8 +319,6 @@ let view3 = NibBasedRootView.loadFromNib() // and another one
```

> 💡 You could also use `MyCustomWidget.loadFromNib()` on a `NibOwnerLoadable` — the same way we just did on `NibLoadable` views above — to load them by code if needs be too.

---


Expand Down
26 changes: 5 additions & 21 deletions Sources/View/NibOwnerLoadable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,38 +41,22 @@ public extension NibOwnerLoadable where Self: UIView {
- returns: A `NibOwnerLoadable`, `UIView` instance
*/
@discardableResult
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method doesn't return any result anymore so @discardableResult can be removed here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@discardableResult and documentation comments too 😄

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, good catch!

static func loadFromNib(owner: Self) -> Self {
func loadNibContent() {
let layoutAttributes: [NSLayoutAttribute] = [.top, .leading, .bottom, .trailing]
for view in nib.instantiate(withOwner: owner, options: nil) {
for view in Self.nib.instantiate(withOwner: self, options: nil) {
if let view = view as? UIView {
view.translatesAutoresizingMaskIntoConstraints = false
owner.addSubview(view)
self.addSubview(view)
layoutAttributes.forEach { attribute in
owner.addConstraint(NSLayoutConstraint(item: view,
self.addConstraint(NSLayoutConstraint(item: view,
attribute: attribute,
relatedBy: .equal,
toItem: owner,
toItem: self,
attribute: attribute,
multiplier: 1,
constant: 0.0))
}
}
}
return owner
}
}

public protocol FrameInitializable: class {
init(frame: CGRect)
}

public extension NibOwnerLoadable where Self: UIView, Self: FrameInitializable {
/**
Returns a `UIView` object instantiated from nib using a default owner instance

- returns: A `NibOwnerLoadable`, `UIView`, `FrameInitializable` instance
*/
static func loadFromNib() -> Self {
return Self.loadFromNib(owner: Self(frame: CGRect.zero))
}
}