LoginSignup
3
5

More than 5 years have passed since last update.

Swiftのメソッドチェーンを使って快適にデザイン適用する

Posted at

この時期になると今年こそAdvent Calender参加すればよかったなー、と思いますが翌年には忘れてて埋まってる、というのを繰り返している。。

普段個人の開発ではコードでデザイン適用をすることが多いのですが、煩雑になりがちなコードをメソッドチェーンを使って快適に記述しようというお話(+それを実現するOSSを作った話)です。

課題: コードによるデザイン適用の煩雑さ

通常、コードでデザインを適用する際には以下のような記述になることが多いと思います。

    let sampleLabel = UILabel()
    sampleLabel.backgroundColor = .yellow
    sampleLabel.text = "sample label"
    sampleLabel.textColor = .green
    sampleLabel.textAlignment = .center
    sampleLabel.font = .boldSystemFont(ofSize: 30.0)
    sampleLabel.numberOfLines = 0

この書き方だと何度もsampleLabel.~ のような記述をするのが煩わしいですし、コードの見通しも悪くなるような気がします。

クロージャーを使うパターン

そこで、例えば自分で以下のようなメソッドを定義して、クロージャー内で対象にスタイルを適用するのは良いテクニックです。

    extension UILabel {
        func apply(_ closure: (_ this: UILabel) -> Void) -> Self {
            closure(self)
            return self
        }
    }

    sampleLabel.apply { this in
        this.backgroundColor(.yellow)
        this.text("sample label")
        this.textAlignment(.center)
        this.textColor(.green)
        this.font(.boldSystemFont(ofSize: 30.0))
        this.numberOfLines(0)
    }

これでもコードをパッと見た時には十分纏まっていて普通に記述するより良さそうです。
ただ、毎回クロージャーを書いて中で記述するのは少し手間です。this.~のように書かないといけないのは通常時と変わっていません。

メソッドチェーンを使うパターン

Swiftではメソッドチェーン使ってさらに簡潔な記述が出来るはずです。

そう考えて作ったのがApplyStyleKitです。
https://github.com/shindyu/ApplyStyleKit
applystylekit.png

ApplyStyleKitを使うと、以下のようにメソッドチェーンを使ってスタイル適用のコードを記述していくことができます。

    sampleLabel.applyStyle
        .backgroundColor(.yellow)
        .text("sample label")
        .textAlignment(.center)
        .textColor(.green)
        .font(.boldSystemFont(ofSize: 30.0))
        .numberOfLines(0)

特徴としては、applyStyle及びその後に続くメソッドがUIViewを直接返すのではなく、UIViewを要素に持つ別のオブジェクト(StyleObjectという名前で定義)を返す点です。

以下のコードがその実装部分になります。


public protocol ApplyStyleProtocol {
    associatedtype StyleCompatible
    var applyStyle: StyleObject<StyleCompatible> { get }
}

extension ApplyStyleProtocol {
    public var applyStyle: StyleObject<Self> {
        return StyleObject(self)
    }
}

public struct StyleObject<Base> {
    let base: Base

    init(_ base: Base) {
        self.base = base
    }
}

あとはUIKitのUIViewやUILabelのプロパティに対応するメソッドをStyleObjectのextensionで泥臭く実装していくだけです。

こうすることで既存のメソッドやプロパティと混ざることなく、.applyStyleとした場合のみ対応するメソッドが使えるようになり、Xcodeの補完を汚すことなくコードを記述することができます。
ApplyStyleKitで記述した結果を通常のスタイル適用コードと見比べると、対象の要素名が一度しか登場しないためスッキリした記述になったと思います。

このインターフェースを実現する方法を考えていたときにRxSwiftの.rxの記述が似ているなと思ってソースコードを見てみると、まさにやりたかったことをとても少ない行数のコードで実現していたので参考にさせてもらいました。

まとめ

  1. メソッドチェーンを用いたスタイル適用コードが記述できるOSSを作りました。実装も特に複雑なことはしていないので、ソースコードを読んで良ければ是非使ってみてください。

  2. 実現したいコードの実装が思いつかないときは、理想に近いインターフェースを提供しているOSSなどのコードを読むと得るものが多かったです。実際、RxSwiftの該当部分のコードを読んだ時は美しくて感動しました。また、目的無くOSSのコードを全て読むのは結構大変ですが、自分のやりたいポイントが分かっていれば、スッと理解できるのでオススメです。

ApplyStyleKitは作ったばかりで対応していないUI Kitも多いのですが、拡張自体は比較的簡単にできると思っています。よければContributeやStarお待ちしています。それでは。

3
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5