ぜのぜ

しりとりしようぜのぜのぜのぜ

Edge.Setが気になったので再現してみた

SwiftUIにはpadding(_ edges: Edge.Set, _ length: CGFloat?)というmodifierがあって,これは第一引数にパディングを適用したい辺を渡せる.その引数は以下のように書けて便利なんだけど,Quick Helpを見るとどちらもpadding(_:_:)を呼んでいるみたいで不思議.ブログネタ(と暇つぶし)に丁度いいので再現してみた.

struct SomeView: View {
    var body: some View {
        Rectangle()
            .padding([.top, .bottom])
            .foregroundColor(.blue)
        Rectangle()
            .padding(.top)
            .padding(.bottom)
            .foregroundColor(.red)
    }
}
SomeView()

実行結果

回答

padding(_:_:)にならって.topの型をCustom.Edge.Setと呼ぶことにする.

Ver.0.5

Custom.Edge.Setインスタンスであるtopstatic変数として定義したはいいが,それを要素とする配列を要素だけのときと同じように振る舞わせる方法が思いつかなかった.ここでは逆に,配列を単体ぽく見えるように実装した.

ただ,これだと重複してるし型も違うので汚い.

enum Custom {
    enum Edge {
        typealias Set = [Element]

        struct Element {
            static let top = Self("top") // (1)
            static let bottom = Self("bottom")

            let kind: String
            init(_ kind: String) {
                self.kind = kind
            }
        }
    }
}

extension Array where Element == Custom.Edge.Element {
    static let top = [Element("top")] // (2)
    static let bottom = [Element("bottom")]
}

func some(_ set: Custom.Edge.Set) {
    print(set)
}

some(.top) // こっちは(2)
some([.top, .bottom]) // こっちの.topは(1)
some([.top, [[.bottom]]]) // error

Ver.1.0

さっきの時点でだいぶ飽きてきたので諦めてEdge.Setの定義を見るとOptionSetというプロトコルに準拠していた.なるほどrawValueにまとめるのね.

それと唐突に,Arrayリテラルで初期化する方法をユーザも使えるんじゃないかと思いついて*1ggったらExpressibleByArrayLiteralを見つけた.

できたのがこれ.

enum Custom {
    enum Edge {
        struct Set: ExpressibleByArrayLiteral {
            typealias ArrayLiteralElement = Self

            let rawValue: Int

            static let top = Self(rawValue: 1 << 0)
            static let bottom = Self(rawValue: 1 << 1)

            init(rawValue: Int) {
                self.rawValue = rawValue
            }

            init(arrayLiteral elements: Custom.Edge.Set...) {
                rawValue = elements.reduce(0) { $0 | $1.rawValue }
            }
        }
    }
}

func some(_ set: Custom.Edge.Set) {
    print(set)
}

some(.top)
some([.top, .bottom])
some([.top, [[.bottom]]])

まとめ

終わってから振り返ると必要だったのはExpressibleByArrayLiteralだけだった気がする.まあ見つかってよかった.

最近の生きる理由は小林さんちのメイドラゴンSです. maidragon.jp

*1:これswift-bookで見たような気もするんよな