[SwiftUI] ViewModifierの作り方

SwiftUI

課題:Rectangle の左上の位置を指定して、配置したい

Rectangle を使っていて、position で位置を指定すると、中央の位置指定になってしまうのが、少し不便でした。

Rectangleの配置指定

position で指定した位置に、小さい円を表示しています。

実装コード

struct ContentView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.yellow)
                .frame(width:100, height:100, alignment: .topLeading)
                .border(Color.red)
                .position(x: 200, y: 100)
            Circle()
                .fill(Color.red)
                .frame(width:10, height:10)
                .position(x:200, y:100)
        }
    }
}

Rectangle を position 指定したときに、左上の位置を指定したい時があるので、簡単に指定できるようにしてみます。

ということで、ViewModifier を作って、便利にしたいと思います。

作りたいModifier

.TLposition(position: CGPoint, size: CGSize)で、Rectangle の左上が指定位置に来るように指定できるようにします。

View Modifier Protocol

ViewModifier というプロトコルを調べてみると、
"func body(content: Self.Content) -> Self.Body" というメソッドを実装する必要がありそうです。

さっそく実装

希望する位置とサイズから、位置を調整して、.position を使うようにしました。

ViewModifier

struct TLPosition: ViewModifier {
    let position: CGPoint
    let size: CGSize
    func body(content: Content) -> some View {
            content
                .frame(width: size.width, height: size.height)
                .position(x: self.position.x + size.width/2,
                          y: self.position.y + size.height/2)
    }
}

ViewModifier の使い方

作成した ViewModifier を使用することで、以下のような表示になります。

ViewModifier使用

作成した ViewModifier は、そのまま、View へ適用することはできずに、.modifier という modifier を使って適用する必要があります。

ViewModifer 使用コード

struct ContentView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.yellow)
                .border(Color.red)
                .modifier(TLPosition(position: CGPoint(x: 200, y: 100),
                                     size: CGSize(width: 100, height: 100)))
                Circle()
                    .fill(Color.red)
                    .frame(width:10, height:10)
                    .position(x:200, y:100)
        }
    }
}

View extension化して、より便利に

.modifier を使っても良いのですが、他の modifier と同じように使うためには、View の extension にする必要があります。

View extension

extension View {
    func tlPosition(position: CGPoint, size: CGSize) -> some View {
        self.modifier(TLPosition(position: position, size: size))
    }
}

こうすることで、他の View modifier と同じように適用することができます。

カスタム View Modifier 使用例

struct ContentView: View {
    var body: some View {
        ZStack {
            Rectangle()
                .fill(Color.yellow)
                .border(Color.red)
                .tlPosition(position: CGPoint(x: 200, y: 100),
                            size: CGSize(width: 100, height: 100)) // ⬅️ NEW!
                Circle()
                    .fill(Color.red)
                    .frame(width:10, height:10)
                    .position(x:200, y:100)
        }
    }
}
注意
View の extension にしていることからもわかりますが、View に適用できる modifier しか使えません。

特定のView のみで使用できる modifier (例えば、Text の multilineTextAlignment ) を使いたいときには、直接その View の extension を作る必要があります。

まとめ: ViewModifier, View extension

このように、ViewModifier を継承して、カスタム modifier を作り、View extension を使うと、コードの可読性が格段に上がります。

うまくつかうと、再利用性、コード可読性、いずれもあげることができますので、うまく使いましょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です