J. Doe
J. Doe

Reputation: 13033

Self type as argument in closure

Is it possible for an instance of a UIView to call a method which executes a closure, and inside that closure referring to the same instance? This is the non-generic version:

import UIKit

public extension UIView {
    func layout(from: (UIView) -> ()) {
        from(self)
    }
}

When I call it with a UILabel for example, I do not have access to e.g. the text aligment. Is it possible that inside the closure I can refer to the UILabel? I would expect something like this would work:

func layout(from: (Self) -> ()) {
    from(self)
}

But it doesn't compile. Is there a workaround? This is what I want:

let label = UILabel(frame: .zero)

label.layout { $0.textAlignment = .natural } // Currenly not working, since $0 = UIView.

Upvotes: 3

Views: 413

Answers (2)

Pentracchiano
Pentracchiano

Reputation: 188

There are different ways to do it.

Firstly, you could use the closures' variable capturing system in order to directly use the variable inside the closure, without passing it as an argument.

public extension UIView {
    func layout(from: () -> ()) {
        from()
    }
}

label.layout { label.textAlignment = .natural }

Otherwise, if you want to pass a generic UIView and change the behaviour accordingly to the specific one - since it looks like you know for sure what type you are working on - you can use a downcast:

public extension UIView {
    func layout(from: (UIView) -> ()) {
        from(self)
    }
}

let label = UILabel(frame: .zero)

label.layout { ($0 as! UILabel).textAlignment = .natural }

Anyway, why are you doing:

label.layout { $0.textAlignment = .natural }

instead of:

label.textAlignment = .natural

Is there any particular reason not to do it? I imagine there's something bigger behind the scenes, I'm just curious.

Upvotes: 1

vadian
vadian

Reputation: 285079

Different approach: Protocol Extension with associated type.

protocol Layout {
    associatedtype View : UIView = Self
    func layout(from: (View) -> ())
}

extension Layout where Self : UIView {
    func layout(from: (Self) -> ()) {
        from(self)
    }
}

extension UIView : Layout {}

let label = UILabel(frame: .zero)
label.layout { $0.textAlignment = .natural }

Upvotes: 3

Related Questions