UIButtonをdeclarativeに扱う


vueとかanglureとか触ってるとSwiftでボタン定義するのすごくだるいなと思う。(mac book airなのでIBは触りたくない)

<button ng-click="ctrl.onClick">anglure</button>
<button v-on:click="say('what')"> vue </button> 

だからButtonクラスを作ってちょっとだけ楽してみた。

final class ViewController: UIViewController {

    private lazy var button = Button(
        title: "click",
        backgroundColor: .blue,
        parent: view,
        onClick: onClickButton
    )

    private var onClickButton = {
        print("tap")
    }
}

ちなみにself参照するときはweakつけないとなーくらいに思ってたんでずけど、


  private var onClickButton = { [weak self] in
       self?.hogehoge()
  }

関数を渡してあげると、buttonが解放されなかった。なんででしょうか・・

final class ViewController: UIViewController {

    private lazy var button = Button(
        title: "click",
        backgroundColor: .blue,
        parent: view,
        onClick: tapButton
    )

    private func tapButton() {
        print("tap")
    }
}

ソースはこれです
final class Button: UIButton {

    convenience init(
        title: String = "",
        color: UIColor = .darkGray,
        backgroundColor: UIColor = .clear,
        parent: UIView? = nil,
        onClick: (() -> Swift.Void)? = nil
        ) {
        self.init(frame: .zero)

        addTarget(self, action: #selector(actionClick), for: .touchUpInside)
        setTitle(title, for: .normal)
        setTitleColor(color, for: .normal)
        self.backgroundColor = backgroundColor
        clickHandler = onClick
        parent?.addSubview(self)
    }

    private var clickHandler: (() -> Swift.Void)?

    @objc private func actionClick() {
        clickHandler?()
    }
}

Swiftでもこうやってかけるようにしてみようかな・・

    private lazy var label = Label(
        textColor: .darkGray,
        parent: view,
        bind: store.state.text
    )

昔ちょこっと試してた初期化方法(おまけ)

lazyで初期化

基本的にはこれで初期化している。Extensionを使ってないから普通にSnipetにしてどこでも使える。

final class ViewController: UIViewController {

    private lazy var button: UIButton = {
        let b = UIButton()
        b.setTitle("button", for: .normal)
        b.addTarget(self, action: #selector(actionTap), for: .touchUpInside)
        ...
        return b
    }()

    @objc private func actionTap() {
        print("button touch up inside")
    }
}

メソッドチェーンで初期化

メソッドチェーンでかけるExtensionを作ってKotlinのスコープ関数のように初期化してみたり

final class ViewController: UIViewController {

    private lazy var button = UIButton().apply { $0
        .setTitle("button", for: .normal)
        .addTarget(self, action: #selector(actionTap), for: .touchUpInside)
        ...
    }

    @objc private func actionTap() {
        print("button touch up inside")
    }
}

 AddTargetのみ省略

RxSwiftのbutton.rx.tapが便利だったので
NotificationCenterやocjcの黒魔術を使ってaddTargetのみ省略したり

final class ViewController: UIViewController {

    private var button = UIButton()

    private func setUpButton() {
        button.setTitle("button", for: .normal)
        button.onTap = {
           print("button touch up inside")
        }
    }
}