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
のインスタンスであるtop
をstatic変数として定義したはいいが,それを要素とする配列を要素だけのときと同じように振る舞わせる方法が思いつかなかった.ここでは逆に,配列を単体ぽく見えるように実装した.
ただ,これだと重複してるし型も違うので汚い.
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で見たような気もするんよな