Alternative to override extension's method - ios

I want to extend UIView by adding some functions, and override them in any subclass of UIView that I want. I found in apple documentations that I can't override extensions (and the compiler will complain) which make some sense. So
I need someone to suggest an alternative way to the below:
extension UIView {
func hide() { //do almost nothing }
}
class myLabel: UILabel {
override func hide() {
//do work on uilabel that can't be done on imgView
}
}
class myImageView: UIImageView {
override func hide() {
//do work on imgView that can't be done on uilabel
}
}
And the reason I want this is that later in my code I will face the below code and I have to many subclasses and I don't want to write too many if-lets trying to cast the view to myLabel, myTextView, myImageView... etc
let view = cell.viewWithTag(someTag)
// and I want to write this below without casting
view.hide()
I tried with protocols and protocol extensions but I couldn't make it though.
Any thoughts?
Note: func hide() is just an example. My func will have more to do.
**Question updated to be clear.

EDIT: Updating answer to make use of protocols also
Protocols does in various ways enable to you replace subclassing in some cases however you still need your class to conform to the protocol to be able to see and override those methods
You can have a protocol for example:
protocol SomeProtocol {
func hide()
}
To do what you are intending to do it is best to have a parent subclass UIView with all functions that can be overridden for example (in this updated answer you can have your methods to override inside the protocol and have your subclasses conform to it):
class ParentView : UIView, SomeProtocol {
func hide() {
print("PARENT")
}
func anyOtherMethod() {
}
}
and then have all the other UIView's that need to override those methods subclass ParentView:
class ViewOne : ParentView {
override func hide() {
print("VIEW ONE")
}
}
class ViewTwo : ParentView {
override func hide() {
print("VIEW TWO")
}
}
So even if you later place this code:
let view = cell.viewWithTag(someTag)
// and I want to write this below without casting
view.hide()
you won't need to explicitly cast your UIView's, the view will call it's intended overridden method, unless and until you call super in your overridden method also
EDIT: More on making use of protocols
In the case you need other controls to also have a hide() method to override then you can still have to subclass, for example in the case of UILabel you need to override it:
class ParentLabel : UILabel, SomeProtocol {
func hide() {
print("PARENT LABEL")
}
}
then you can write the intended code with casting to your protocol
if let view = cell.viewWithTag(someTag) as? SomeProtocol {
view.hide() // prints PARENT LABEL
}
and either use that subclassed UILabel control or if you need in some cases some label to override that behavior then you can still create a child subclass of ParentLabel:
class LabelOne : ParentLabel {
override func hide() {
print("LABEL ONE")
}
}

Related

Instantiate the delegate pattern one time or more in Swift?

I am studying delegate pattern for swift on below code. I can not be pretty sure how can I use this option "without the need to reinstantiate the view.
protocol ShapeViewDelegate {
func drawShapeView(_ shapeView: ShapeView)
}
class ShapeView: UIView {
var strokeColor: UIColor?
var fillColor: UIColor?
var delegate: ShapeViewDelegate? {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
delegate?.drawShapeView(self) // self means object( ShapeView() ) is it instantiated ?
}
}
View object supposed to ready coming from delegate object but I didn't instantiate it, where this object instantiated using protocol automatically instantiated it at the run time. So I am writing an example like this :
class ShapeViewController: ShapeViewDelegate {
drawShapeView(view)
}
View is instantiated in other words occupied the memory at this example ?
You have to define drawShapeView() in ShapeViewController. In your code you are using or calling drawShapeView() which is already being called in your ShapeView. And about instantiate, yes you need to instantiate ShapeView in ShapeViewController or any other place you are confirming the delegate.
Code in your ShapeViewController -
class ShapeViewController: ShapeViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let shape = ShapeView(...)
shape.delegate = self
view.addSubView(shape)
}
func drawShapeView(_ shapeView: ShapeView) {
//your code here
}
}
Via this feature, you can have many definitions of ShapeView based on different ShapeViewController instances without worrying to modify ShapeView.

Delegating action through protocol not working swift

I needed to delegate a click action for my UIView class to my UIViewController class since Swift does not support multiple class inheritance. So i wanted it such that once a button is clicked on my subview, a function in my BrowserViewController class is called.
I am using a protocol to achieve this, but on the function does not triggered when the button is tapped. Please help me out.
View Controller
class BrowseViewController: UIViewController {
var categoryItem: CategoryItem! = CategoryItem() //Category Item
private func setupExplore() {
//assign delegate of category item to controller
self.categoryItem.delegate = self
}
}
// delegate function to be called
extension BrowseViewController: ExploreDelegate {
func categoryClicked(category: ProductCategory) {
print("clicked")
let categoryView = ProductByCategoryView()
categoryView.category = category
categoryView.modalPresentationStyle = .overCurrentContext
self.navigationController?.pushViewController(categoryView, animated: true)
}
}
Explore.swift (subview)
import UIKit
protocol ExploreDelegate: UIViewController {
func categoryClicked(category: ProductCategory)
}
class Explore: UIView {
var delegate: ExploreDelegate?
class CategoryItem: UIView {
var delegate: ExploreDelegate?
var category: ProductCategory? {
didSet {
self.configure()
}
}
var tapped: ((_ category: ProductCategory?) -> Void)?
func configure() {
self.layer.cornerRadius = 6
self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.categoryTapped)))
self.layoutIfNeeded()
}
#objc func categoryTapped(_ sender: UIGestureRecognizer) {
delegate?.categoryClicked(category: ProductCategory.everything)
self.tapped?(self.category)
}
}
}
Simply add a print statement inside categoryTapped.
You will then know if it is actually being tapped.
A million things could go wrong, for example, you may have forget to set the UIView to allow intertaction.
After checking that. Next add another print statement inside categoryTapped which shows you whether or not the delegate variable is null.
You'll quickly discover the problem using simple print statements.
print("I got to here!")
It's that easy.
And what about
if delegate == nil { print("it is nil!! oh no!" }
else { print("phew. it is NOT nil.") }
Debugging is really that easy at this level.
Next add a print statement inside setupExplore()
func setupExplore() {
print("setup explore was called")
....
See what happens.
I don't see any piece of code which sets the delegate.
First of all, define delegate as a property inside CategoryItem class, Then you must set the current instance of BrowseViewController to the delegate variable of CategoryItem. Now you can expect your method being called.
There are a few things that could cause the delegate method to not be triggered in this code:
Ensure that isUserInteractionEnabled = true on your CategoryItem. This is probably best done in either the configure() function in the CategoryItem or in the setupExplore() function of the BrowseViewController.
Make sure that the setupExplore() function on the BrowseViewController is being called, and that the category is being set on the CategoryItem to trigger the configure function. Otherwise, either the delegate or the gesture recognizer might not being set.
Side Note - weak vs strong delegate
On a side note, it is usually best practice to make your delegate properties weak var rather that having them be a strong reference, as this makes them prone to strong retain cycles.
Therefore, you might want to consider making the var delegate: ExploreDelegate? on your CategoryItem into weak var delegate: ExploreDelegate?. For more information on this problem, view this post.

Requiring a UIViewController subclass to override a specific method

I've got a superclass UIViewController class that implements a couple of default methods, e.g.:
class Super: UIViewController {
override func viewDidLoad () {
setupView()
}
func setupView () {
initToolbar()
setPageTitle()
}
func initToolbar () {
// some code..
}
func setPageTitle () {
// nothing?
}
}
and subclasses that inherit from Super:
class Sub: Super {
override func setPageTitle () {
self.title = "custom title"
}
}
I'd like to force all subclasses to override the setPageTitle() method (forcing a compile time error if no implementation is present). However, the only way I've managed to achieve this is providing a default implementation in the Super class that contains an assert statement, causing the app to crash if it has not been overriden. This is not really what I was after as the error is only present at runtime and ideally i'd like a compile time warning/error if the method has no implementation. Is there any way to set this method to be overriden as a requirement? Similar to abstract methods in other languages?
I thought about using protocols & extensions but it looks like with extensions I can't override the viewDidLoad method and this is necessary for the superclass.
Any ideas?

How to override 'UIScrollView.delegate'?

When I look at UITableView, I find that UITableView 's UITableViewDelegate inherit from UIScrollViewDelegate in order to override UIScrollView's delegate
Here is how i implement my own subclass of 'UIScrollView'.
protocol UIFormViewDelegate:NSObjectProtocol, UIScrollViewDelegate{
}
class UIFormView: UIScrollView {
override var delegate:UIFormViewDelegate?
}
The problem is that the compiler gives a warning:
Property 'delegate' with type 'UIFormViewDelegate?' cannot override a property with type 'UIScrollViewDelegate?'
Can anybody tell me what's wrong here?
You cannot override the type of delegate
What you can do is something like that
class DelegateClass: NSObject, UIFormViewDelegate {
}
class UIFormView: UIScrollView {
func setUpDelegate() {
self.delegate = DelegateClass()
}
}
And then call the setUpDelegate from somewhere where it makes sense

Overriding delegate property of UIScrollView in Swift (like UICollectionView does)

UIScrollView has a delegate property which conforms to UIScrollViewDelegate
protocol UIScrollViewDelegate : NSObjectProtocol {
//...
}
class UIScrollView : UIView, NSCoding {
unowned(unsafe) var delegate: UIScrollViewDelegate?
//...
}
UICollectionView overrides this property with a different type UICollectionViewDelegate
protocol UICollectionViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class UICollectionView : UIScrollView {
unowned(unsafe) var delegate: UICollectionViewDelegate?
//...
}
When I try to override UIScrollViews delegate with my protocol like so:
protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class MyScrollView: UIScrollView {
unowned(unsafe) var delegate: MyScrollViewDelegate?
}
the compiler gives me two warnings:
Property 'delegate' with type 'MyScrollViewDelegate?' cannot override a property with type 'UIScrollViewDelegate?'
'unowned' cannot be applied to non-class type 'MyScrollViewDelegate?'
How can I subclass UIScrollView and override type of delegate property (i.e. use a custom delegate protocol) ?
I think overriding an inherited property is something that's possible in Objective-C but not (at least currently) in Swift. The way I've handled this is to declare a separate delegate as a computed property of the correct type that gets and sets the actual delegate:
#objc protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
func myHeight() -> CGFloat
// ...
}
class MyScrollView: UIScrollView {
var myDelegate: MyScrollViewDelegate? {
get { return self.delegate as? MyScrollViewDelegate }
set { self.delegate = newValue }
}
}
This way anything that calls the scroll view delegate normally still works, and you can call your particular delegate methods on self.myDelegate, like this:
if let height = self.myDelegate?.myHeight() {
// ...
}
You can do like this:
protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var myDelegate: ExtendedScrollViewDelegate?
override weak var delegate: UIScrollViewDelegate? {
didSet {
myDelegate = delegate as? ExtendedScrollViewDelegate
}
}
}
Hope this helps
My favoured method personally is not to subclass scrollviews directly but to make a UIView subclass containing and acting as delegate for a separate scrollview, then forward that scrollview's delegate messages on to the UIView subclass's own delegate where necessary. This also allows for the adding of custom controls outside of the area defined by the scroll view. It may seem a little inelegant compared to a direct subclass, but it does at least avoid unpleasant hacks.
Here is a solution for changing the type of the overriding properties in Swift. It is especially useful when you need to extend protocols of delegates.
#objc protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var delegateInterceptor: ExtendedScrollViewDelegate?
override var delegate: UIScrollViewDelegate! {
didSet {
if let newValue = delegate {
let castedDelegate = unsafeBitCast(delegate, ExtendedScrollViewDelegate.self)
delegateInterceptor = castedDelegate
}
else {
delegateInterceptor = nil
}
}
}
}
This works as tested with Swift version 1.2. I hope it helps.
You can override get and set method by declare function like:
func setDelegate(delegate:UITableViewDelegate?){
self.delegateInterceptor = delegate;
}
swift compiler the property to method as Objective-c does.
Consider the following situation:
class BaseProp {}
class Base {
var prop: BaseProp
}
Then if you do this:
class DerivedProp: BaseProp {}
class Derived: Base {
override var prop: DerivedProp
}
Then if would break the subclassing principles (namely, the Liskov Substitution Principle). Basically what you are doing is limiting the scope of "var prop" from wider "BaseProp" type to a more narrow "DerivedProp" type. Then this kind of code would be possible, which does not make sense:
class UnrelatedProp: BaseProp {}
let derived = Derived()
let base = derived as Base
base.prop = UnrelatedProp()
Note that we are assigning an instance of UnrelatedProp to the property, which does not make sense for the Derived instance which we actually operate with. ObjectiveC allows such kind of ambiguity, but Swift doesn't.

Resources