Reputation: 2611
I'm trying to extract some of the code base for re use purpose. My approach is using Protocol
and Protocol
Extension
instead of general BaseClass
.
I have create the below a protocol
and protocol extension
protocol MovieDisplay {
var collectionView: UICollectionView! { get set }
var refreshControl: UIRefreshControl! { get set }
}
extension MovieDisplay where Self: UIViewController {
var refreshControl: UIRefreshControl {
let rc = UIRefreshControl()
rc.backgroundColor = .clear
rc.tintColor = .lightGray
if #available(iOS 10.0, *) {
collectionView.refreshControl = rc
} else {
// Fallback on earlier versions
collectionView.addSubview(rc)
}
return rc
}
}
In my main class that adopt the protocol I declare like this (using default implementation of refreshcontrol
)
class PopularMovieVC: UIViewController, MovieDisplay {
@IBOutlet weak var collectionView: UICollectionView!
}
The problem is function which involve refreshcontrol
does not work. It works only when I explicitly declare refreshcontrol
variable inside main class and convert extension into function and call it inside main class like below:
func setupRefreshControl() {
refreshControl.backgroundColor = .clear
refreshControl.tintColor = .lightGray
if #available(iOS 10.0, *) {
collectionView.refreshControl = refreshControl
} else {
// Fallback on earlier versions
collectionView.addSubview(refreshControl)
}
}
How to properly configure the protocol
and protocol extension
for default implementation?
Upvotes: 0
Views: 259
Reputation: 299485
Your protocol requires a gettable and settable refreshControl
(that returns UIRefreshControl!
), but your default implementation only provides a getter (and that getter returns a different type, UIRefreshControl
). Your default implementation also returns a different UIRefreshControl
every time it is accessed, and modifies collectionView
every time it's accessed. None of this, I think, is what you mean.
As vadian notes, I think a base class is what you really want here, if you want to modify collectionView.refreshControl
automatically. Conforming to a protocol should never cause implicit changes to other properties, and in most cases it can't. Imagine if PopularMovieVC
were conformed to MovieDisplay
in an extension in another module. That would lead to confusion at best.
Protocol conformance extends how a type can be used, such as adding new methods. It doesn't change anything about the type itself. If you want to change something about the type itself, you need something like inheritance or composition, not protocol conformance.
If you're doing a bit of this, I wouldn't do it with protocols this way. I'd just create extensions like this:
extension UIRefreshControl {
static func makeStandard(attachedTo collectionView: UICollectionView) -> UIRefreshControl {
let rc = UIRefreshControl()
rc.backgroundColor = .clear
rc.tintColor = .lightGray
if #available(iOS 10.0, *) {
collectionView.refreshControl = rc
} else {
// Fallback on earlier versions
collectionView.addSubview(rc)
}
return rc
}
}
extension UIActivityIndicatorView {
static func makeStandard() -> UIActivityIndicatorView {
return UIActivityIndicatorView(style: .gray)
}
}
Then your view controller might look like:
class MyViewController: UIViewController {
private var refreshController: UIRefreshControl!
@IBOutlet var collectionView: UICollectionView!
let activityIndicator = UIActivityIndicatorView.makeStandard()
override func viewDidLoad() {
refreshController = .makeStandard(attachedTo: collectionView)
}
}
No protocols needed, and this allows you to handle multiple collection views in the same view controller, or any other unusual situation. It also makes it clearer that calling this method will modify the collection view.
Upvotes: 1
Reputation: 285170
It doesn't work because the computed property isn't called implicitly.
Adding this line in viewDidLoad
should initialize the refresh control
_ = refreshControl
In this case I'd really prefer a base class
Upvotes: 1