alisoftware / reusable Goto Github PK
View Code? Open in Web Editor NEWA Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)
License: MIT License
A Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)
License: MIT License
in Cartfile add this > github "AliSoftware/Reusable"
$ carthage update
*** Downloading Reusable.framework binary at "4.0.5"
*** Skipped installing Reusable.framework binary due to the error:
"Incompatible Swift version - framework was built with 4.2 (swiftlang-1000.11.37.1 clang-1000.11.45.1) and the local version is 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1)."
check it plz
Via Carthage:
Dependency "Reusable" has no shared framework schemes for any of the platforms: tvOS
I followed the readme and created MyCell.swift
, MyCell.xib
, changed prototype class and reusable identifier in storyboard to MyCell
. I think I don't need to call register in controller any more, am I right? I tried to register in viewDidLoad
of my controller. It worked, but, the cells don't trigger segues any more. I assume it's been overwritten by the register. What did I do wrong?
Hope Reusable to be better
In ios8 ViewController with xib oulets is nil if invoke init
method..
I encountered this problem in my app and realized it's also there in the example app.
It's easy to reproduce by launching the example app in the simulator and selecting "Details" on a table view cell.
The error reads fatal error: The viewController 'InfoDetailViewController' of 'nil' is not of class 'InfoDetailViewController'
Reusable/Sources/View/NibOwnerLoadable.swift:40:28: error: 'NSLayoutAttribute' has been renamed to 'NSLayoutConstraint.Attribute'
There's an error when compiling against Xcode 10 beta 6
Hey Oliver, I ran into an issue designing my API and wanted some advice. With many of my view controllers, I require dependencies for the controller to function properly. If I use the instantiate
function vended by your library, I create another function that will fill out a bunch of implicitly wrapped optionals, like so:
class MyVC: UIViewController {
private var userService: UserService!
static func instantiate(with userService: UserService) -> Self {
let vc = Self.instantiate() // from Reusable
vc.userService = userService
return vc
}
}
have you come up with a better pattern? Or are you doing something similar
Steps to reproduce
Test01
).Test01
to target iOS 11.Test01.workspace
).Expected behavior
Actual behavior
Notes
DerivedData
folder (delete it via bash script) every time before I startup a different version of Xcode.Hello :)
I'm trying to use the UICollectionView's dequeueReusableSupplementaryView(ofKind:for:viewType) function but I struggle with the viewType parameter.
From the function definition, the viewType parameter must be a UICollectionReusableView, and also a Reusable.
This first snippet compiles correctly:
let headerType = TitleHeaderView.self
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, for: indexPath, viewType: headerType)
But this next one doesn't compiles and gives me a Argument 'viewType' must precede argument 'for'
error.
//This func is responsible for returning the correct header class according to the given indexPath
func retrieveHeaderType() -> (UICollectionReusableView & Reusable).Type { ... }
let headerType = retrieveHeaderType()
let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, for: indexPath, viewType: headerType)
As I understand it, this error comes up because my headerType
doesn't have the func's required type, and the compilator think I want to use the classic dequeue func.
But isn't a UICollectionReusableView & Reusable
a correct type for dequeueReusableSupplementaryView(ofKind:for:viewType)
?
What type should my headerType
func return in order to work?
Reusable version: 4.0.2 (imported with cocoapods)
Xcode 9.3
App project in Swift 4.1
Reusable project in Swift 3.3
i want to subclass a view controller but cannot override sceneStoryboard property.
Both parent and child viewControllers are from different storyboards
Using the library not only in the "main" app but also in a Share extension, I am getting a warning
inking against a dylib which is not safe for use in application extensions: Carthage/Build/iOS/Reusable.framework/Reusable
The code probably only uses APIs that are "extension-safe", so it would be nice to check the box in the project settings to make the warning disappear.
I'm getting this error running a Carthage update:
Skipped installing Reusable.framework binary due to the error
Incompatible Swift version - framework was built with swiftlang-800.0.63 clang-800.0.42.1 and the local version is swiftlang-802.0.53 clang-802.0.42.
Any idea?
Is there any Swift 3 support?
Hey, I see the loadFromNib-method. Is there any way to integrate a UIView via code and storyboard?
A view class can be instantiated from multiple nibs, even if that's not really common. I have 2 suggestions to cover this kind of situations:
nib
to defaultNib
because it may have multiple nibs.loadFromNib()
to:static func instantiateFromNib(
name: String? = nil,
bundle: NSBundle = NSBundle.mainBundle(),
owner: AnyObject? = nil,
options: [NSObject: AnyObject]? = nil)
-> Self?
(I also renamed load
to instantiate
to keep consistent with Storyboard protocols.)
I can make this improvement if you are OK with it.
Great library, thanks for sharing. :)
And setup Circle-CI to run them
In your example of Customize reuseIdentifier, nib, etc for non-conventional uses
static var nib: UINib { return UINib(nibName: "VeryCustomUI", bundle: nil) } // Use VeryCustomUI.xib
The bundle should be a parameter and could look like this instead
static var nib: UINib { return self.nib(bundle: nil) } // Use VeryCustomUI.xib
static func nib(bundle: Bundle?) -> UINib { return UINib(nibName: "VeryCustomUI", bundle: bundle) } // Use VeryCustomUI.xib
The reason behind this is that, if an app is using custom localizations, the way to get the localized version of a Nib is by doing this:
let path = Bundle.main.path(forResource: "SomeResource", ofType: "lproj")!
let bundle = Bundle(path: path)!
let nib = UINib(nibName: "VeryCustomUI", bundle: bundle)
// example VC
class SampleViewController: UIViewController {
init(bundle: Bundle?) {
super.init(nibName: "SampleViewController", bundle: bundle)
}
}
// example UITableViewCell
let name: String = "SampleTableViewCell"
tableView.register(
UINib(nibName: name, bundle: sampleBundle),
forCellReuseIdentifier: name)
It would be great if you could do this.
Not that it doesn't work as is, but the Xcode warning ('Conversion to Swift 5 is available') that appears when using Reusable with Cocoapods is rather annoying.
Hi,
I have updated my Xcode and sine the Reusable module does't compile. The error message is :
Module file was created by an older version of the compiler; rebuild 'Reusable' and try again.
The Xcode version is : 7.2.1
Hi,
First of all thank you very much for such a useful pod! I used to do this manually but now having it on a pod is definitely more reliable :D
One question though, is there any plans to support the new API instantiateViewController(identifier:creator:)
?
TIA
Hi, 😀
I had an idea and I would like to present it before I attempt a PR so we can discuss it first.
I am really not fan of that line:
let colorViewController = self.storyboard?.instantiateViewController(withIdentifier: "identifier") as! ColorViewController
So having a bit of play I came up with an idea (which is pretty simple and swifty .. 😀)
extension UIStoryboard {
func instatiate<T: UIViewController>(type: T.Type, with identifier: String) -> T {
return instantiateViewController(withIdentifier: identifier) as! T
}
}
So the following can be possible:
let colorViewController = storyboard?.instatiate(type: ColorViewController.self, with: "identifier")
Also must of the times I use the same name of the class as a storyboard identifier, so we can improve it like that:
extension UIStoryboard {
func instatiate<T: UIViewController>(type: T.Type, identifier: String? = nil) -> T {
let identifier = identifier ?? "\(type.self)"
return instantiateViewController(withIdentifier: identifier) as! T
}
}
and can be used like that:
let colorViewController = storyboard?.instatiate(type: ColorViewController.self)
As a result a protocol conformance is no longer required. 😀
This is just a draft of my thoughts but please let me know if you believe that Reusable
can facilitate this approach.
Thanks for good work. Then a new swift version has appeared, please for another upgrade as soon as possible?
Hi.
When this plugin registered inherited cell, it does dequeueReusableCell
as its supperclass.
In this case, it will occur crash because registered class and dequeued class is different.
class BaseCell: UICollectionViewCell, Reusable {}
class MyCell: BaseCell {}
let cellType: BaseCell.Type = MyCell.self
class CollectionViewController: UICollectionViewController {
override func viewDidLoad() {
// it is registered as "BaseCell". But I think this cell should be registered as "MyCell".
collectionView.registerReusableCell(cellType)
}
override func collectionView(collectionView: UICollectionView,
cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
// in this statement, the cell is "MyCell". (will be crashed)
let cell = collectionView.dequeueReusableCell(indexPath: indexPath, cellType: cellType)
return cell
}
// ...
}
If possible, I will send a pull-request. :)
Thanks.
When working with this library when trying to put NibOwnerLoadable
views into a UICollectionViewCell
and UITableViewCell
, I found that to get the correct view hierarchies, these extensions were required:
public extension NibOwnerLoadable where Self: UICollectionViewCell {
func loadNibContent() {
let layoutAttributes: [NSLayoutConstraint.Attribute] = [.top, .leading, .bottom, .trailing]
for case let view as UIView in Self.nib.instantiate(withOwner: self, options: nil) {
view.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(view)
NSLayoutConstraint.activate(layoutAttributes.map { attribute in
NSLayoutConstraint(
item: view, attribute: attribute,
relatedBy: .equal,
toItem: contentView, attribute: attribute,
multiplier: 1, constant: 0.0
)
})
}
}
}
public extension NibOwnerLoadable where Self: UITableViewCel {
func loadNibContent() {
let layoutAttributes: [NSLayoutConstraint.Attribute] = [.top, .leading, .bottom, .trailing]
for case let view as UIView in Self.nib.instantiate(withOwner: self, options: nil) {
view.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(view)
NSLayoutConstraint.activate(layoutAttributes.map { attribute in
NSLayoutConstraint(
item: view, attribute: attribute,
relatedBy: .equal,
toItem: contentView, attribute: attribute,
multiplier: 1, constant: 0.0
)
})
}
}
}
Note the change above from self
to contentView
. This works if the root view is a plain UIView
object in the .xib
(but doesn't if they're UICollectionViewCell or UITableViewCell). However, I'd love to be able to do something cleaner like:
protocol ContentViewNibLoadable {
var contentView: UIView { get }
}
extension ContentViewNibLoadable where Self: NibOwnerLoadable {
func loadNibContent() {
...
}
}
extension UICollectionViewCell: ContentViewNibLoadable { }
extension UITableViewCell: ContentViewNibLoadable { }
but this doesn't resolve to the correct contentView
property in the cell. Any ideas on how to make this more elegant?
I have been pointed to this lib after having created almost the same thing for one of our projects. I have done a couple of things differently, so I thought sharing them here might lead to a productive discussion:
public protocol Loadable {
static var nib: UINib { get }
}
public protocol Reusable {
static var reuseIdentifier: String { get }
}
public protocol LoadableCell: Reusable, Loadable {}
Having no direct relation between Loadable
and Reusable
allows for usage with UIViews
designed in xib:
public extension Loadable where Self: UIView {
static func loadFromNib() -> Self {
return nib.instantiateWithOwner(nil, options: nil).first as! Self
}
}
This is of course possible with NibReusable
as well, but for simple loading of UIView
from UINib
name Loadable
is a better fit.
Thoughts?
I tend to avoid the use of force-unwrapping !
and force-casting as!
.
I think it's preferable to use guard let x = y as? Y else { fatalError("…") }
and use an explicit and helpful error in the fatalError(…)
call rather than let x = y as! Y
There are not many force-casts in the codebase so that should be pretty quick to do.
This library is obviously concentrated on UITableView
and UICollectionView
support but MKMapView
has a lot of the same concepts: registering annotations and dequeuing them.
I currently use a simple extension for that
extension MKMapView {
final func register<T: MKAnnotationView>(annotationType: T.Type)
where T: Reusable {
self.register(annotationType.self, forAnnotationViewWithReuseIdentifier: annotationType.reuseIdentifier)
}
final func dequeueReusableAnnotation<T: MKAnnotationView>(annotationType: T.Type = T.self) -> T
where T: Reusable {
guard let view = self.dequeueReusableAnnotationView(withIdentifier: annotationType.reuseIdentifier) as? T else {
fatalError(
"Failed to dequeue a annotation view with identifier \(annotationType.reuseIdentifier) matching type \(annotationType.self). "
+ "Check that the reuseIdentifier is set properly in your XIB/Storyboard "
+ "and that you registered the annotation view beforehand"
)
}
return view
}
}
I wonder if you be willing to add MKMapView
support. I can provide this extension as a PR together with some documentation and maybe even a sample.
This project now supports CocoaPods and Swift Version Manager.
Could you add Carthage installation support to this project?
Thanks.
The use of Self fails in the presence of subclassed cells because String(Self) refers to the class directly conforming to the protocol, while String(self) refers correctly to the class of the instance.
I've setup unit tests for my project and want to add View/ViewController instantiation to the tests because the project is modular. When compiling this error message appears when for any methods that came from Reusable.
Undefined symbols for architecture x86_64:
"static (extension in Reusable):Reusable.StoryboardSceneBased< where A: __C.UIViewController>.instantiate() -> A", referenced from:
implicit closure #1 () throws -> FooProject.HomeViewController? in FooProjectTests.InstantiationTests.testInstantiations() -> () in InstantiationTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I'm not sure why this is happening because the other frameworks can be accessed easily. This is a sample code that generates the error
@testable import FooProject
import Nimble
import XCTest
class InstantiationTests: XCTestCase {
func testInstantiations() {
expect(HomeViewController.instantiate()).notTo(raiseException())
}
}
Is the project enabled for testability
Yes
Do you have other tests besides this?
Yes
What do you mean by the project being modular?
The project has multiple Targets and Configurations. ex
Configurations
Targets
If not, what is the recommended way to use NibOwnerLoadable on a cell(table/collection) class.
It would be great to make this repository Carthage-compatible.
on trying below getting erros
`class ParentCell: UITableViewCell, Reusable {}
class Child1Cell: ParentCell {}
class Child2Cell: ParentCell {}
func cellType(for indexPath: NSIndexPath) -> ParentCell.Type {
return indexPath.row.isMultiple(of: 2) ? Child1Cell.self : Child2Cell.self
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellClass = self.cellType(for: indexPath)
// As self.cellType(for:)
always returns a ParentCell
(sub-)class, the type
// of the variable cell
below is infered to be ParentCell
too. So only methods
// declared in the parent ParentCell
class will be accessible on the cell
variable.
// But this code will still dequeue the proper type of cell (Child1Cell or Child2Cell).
let cell = tableView.dequeueReusableCell(for: indexPath, cellType: cellClass)
// Then fill the content of your cell (using methods/properties from ParentCell
type)
return cell
}`
<2020-03-08 20:37:13.830457+0500 ReusableDemo iOS[18026:477363] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempted to dequeue multiple cells for the same index path, which is not allowed. If you really need to dequeue more cells than the table view is requesting, use the -dequeueReusableCellWithIdentifier: method (without an index path). Cell identifier: MyXIBTextCell, index path: <NSIndexPath: 0x8edbb09a3c0860b8> {length = 2, path = 1 - 0}>
I've added extensions for UITableViewCell, UITableViewHeaderFooterView, UICollectionViewCell and UICollectionReusableView in the protocol file:
extension UITableViewCell: Reusable {}
extension UITableViewHeaderFooterView: Reusable {}
extension UICollectionViewCell: Reusable {}
extension UICollectionReusableView: Reusable {}
So that I don't need to make all my custom cells/views conform to Reusable
, and can assume all custom cells/views will be Reusable
. This works, however, the compiler is complaining about the UICollectionViewCell
with "Conformance of 'UICollectionViewCell' to protocol 'Reusable' was already stated in the type's module 'UIKit'".
I can't see how the collection view cell is trying to use Reusable
in UIKit. As well, I tried changing the name of the protocol to ReusableView
and it still complained. I also tried adding the name of my module before Reusable, so it became: extension UICollectionViewCell: MyModule.Reusable {}
.
Any ideas how to make the compiler be quiet about this?
Thanks
Hi. I made a NibLoadable view and added subviews.
However, those views are nil inside required init?(coder:) so I can't setup my views. (e.g. I want to set corner radius to a subview)
Where can I put additional init logic for NibLoadable?
I met a lot of errors in Pods.
I ran across an issue where Swift wasn't allowing me to supply a type that was produced from a function
func classTypeForTable(tableView: UITableView) -> ReusableCell.Type {
return MyCell.Type
}
// In cellForRow //
let cellClass = classTypeForTable(tableView)
let cell = tableView.dequeueReusableCell(indexPath) as cellClass
^^^^^^ This last line fails because "cellClass is not a type"
I was able to get around this by changing the dequeue function and adding another:
func dequeueReusableCell<T: UITableViewCell where T: Reusable>(indexPath indexPath: NSIndexPath) -> T {
return self.dequeueReusableCell(indexPath, cellType: T.self)
}
func dequeueReusableCell<T: UITableViewCell where T: Reusable>(indexPath: NSIndexPath, cellType: T.Type) -> T {
return self.dequeueReusableCellWithIdentifier(T.reuseIdentifier, forIndexPath: indexPath) as! T
}
This would seem like a function you might want to add for advanced users...
NibOwnerLoadable
uses forEach
method which is introduced in iOS 9.3. I suppose this would be a problem for people supporting iOS 8.
What does NibOwnerLoadable
do anyway? Why is it adding layout constraints to the instantiated view?
Hi,
As the titles says. I've been using this approach for some time:
public extension UITableView {
/// Registers and dequeues a `Reusable` `UITableViewCell`.
///
/// - Returns: A reusable cell.
final func reusableCell<T: UITableViewCell>() -> T where T : Reusable {
guard let cell = self.dequeueReusableCell(withIdentifier: T.reuseIdentifier) as? T else {
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier) as! T
}
return cell
}
}
Then:
let cell = tableView.reusableCell() as TableViewCell
No need to remind myself to register beforehand. I know the signatures don't match. It's just to get an idea.
Is there some edge case I'm not seeing where this might not be possible/advisable?
Thanks
Consider the following classes:
class MyCustomClass: UIView, NibOwnerLoadable {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.loadNibContent()
// ... rest of init
}
}
class MyCustomSubClass: MyCustomClass {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// ... rest of init
}
}
What happens now when instantiating MyCustomSubClass
is that MyCustomClass
's nib file will be loaded instead of MyCustomSubClass
's one.
My guess is that this has something to do with the way the nib is loaded in Sources/View/NibOwnerLoadable.swift#L41
. Self.nib
somehow will be the the base class's nib file and not resolve to the actual class this is called on.
Hi!
I'm stuck with instantiating controllers from Storyboards. In my project, I have several storyboards with multiple scenes(controllers) in each storyboard.
Intro.storyboard (IntroViewController, SpalshViewController)
Main.storyboard (ViewController1, ViewController2, etc)
In my ViewController I use protocol StoryboardSceneBased, but obviously, it is not working.
Does Reusable ability to instantiate controllers like I need?
This can be used together?
Cause one place use xib, another use code.
Are there any intentions to extend Reusable to macOS, specifically NSTableView
/NSCollectionView
? The library is obviously focused on iOS/UIKit but I thought I'd still ask :). Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.