SwiftUIの基本を身につけたい方はこちら

【SwiftUI】モディファイアを自分で作る方法~カスタムモディファイア~

この記事では、モディファイアを自作する方法を紹介していきたいと思います。

カスタムモディファイアとは?

モディファイアというのは、オブジェクトに対して、メソッドチェーンのように、.foregroundColor(.white).backgroundColor(.red).padding()など.~~~でレイアウトなどを変更するコードのことです。

カスタムモディファイアとは、これを自作するということです。

カスタムモディファイアの作り方

では、実際に作っていきましょう。

例えば、このようなレイアウトを作るとしましょう。

普通に書くと、以下のように長いコードになってしまいます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
}) {
Text("Swift")
.padding()
.frame(width: 300, height: 60)
.background(.orange)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
Button(action: {
}) {
Text("Java")
.padding()
.frame(width: 300, height: 60)
.background(.orange)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
Button(action: {
}) {
Text("Python")
.padding()
.frame(width: 300, height: 60)
.background(.orange)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
Button(action: {
}) {
Text("PHP")
.padding()
.frame(width: 300, height: 60)
.background(.orange)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
}
}
}
struct ContentView: View { var body: some View { VStack { Button(action: { }) { Text("Swift") .padding() .frame(width: 300, height: 60) .background(.orange) .foregroundColor(.white) .font(.title) .cornerRadius(30) } Button(action: { }) { Text("Java") .padding() .frame(width: 300, height: 60) .background(.orange) .foregroundColor(.white) .font(.title) .cornerRadius(30) } Button(action: { }) { Text("Python") .padding() .frame(width: 300, height: 60) .background(.orange) .foregroundColor(.white) .font(.title) .cornerRadius(30) } Button(action: { }) { Text("PHP") .padding() .frame(width: 300, height: 60) .background(.orange) .foregroundColor(.white) .font(.title) .cornerRadius(30) } } } }
struct ContentView: View {
    var body: some View {
        VStack {
            Button(action: {
                
            }) {
                Text("Swift")
                    .padding()
                    .frame(width: 300, height: 60)
                    .background(.orange)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(30)
            }
            Button(action: {
                
            }) {
                Text("Java")
                    .padding()
                    .frame(width: 300, height: 60)
                    .background(.orange)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(30)
            }
            Button(action: {
                
            }) {
                Text("Python")
                    .padding()
                    .frame(width: 300, height: 60)
                    .background(.orange)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(30)
            }
            Button(action: {
                
            }) {
                Text("PHP")
                    .padding()
                    .frame(width: 300, height: 60)
                    .background(.orange)
                    .foregroundColor(.white)
                    .font(.title)
                    .cornerRadius(30)
            }
        }
    }
}

上記のコードをカスタムモディファイアを使って書くと以下のようにかなり短くなります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
}) {
Text("Swift")
.modifier(MainButtonModifier())
}
Button(action: {
}) {
Text("Java")
.modifier(MainButtonModifier())
}
Button(action: {
}) {
Text("Python")
.modifier(MainButtonModifier())
}
Button(action: {
}) {
Text("PHP")
.modifier(MainButtonModifier())
}
}
}
}
struct MainButtonModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.frame(width: 300, height: 60)
.background(.orange)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
}
struct ContentView: View { var body: some View { VStack { Button(action: { }) { Text("Swift") .modifier(MainButtonModifier()) } Button(action: { }) { Text("Java") .modifier(MainButtonModifier()) } Button(action: { }) { Text("Python") .modifier(MainButtonModifier()) } Button(action: { }) { Text("PHP") .modifier(MainButtonModifier()) } } } } struct MainButtonModifier: ViewModifier { func body(content: Content) -> some View { content .padding() .frame(width: 300, height: 60) .background(.orange) .foregroundColor(.white) .font(.title) .cornerRadius(30) } }
struct ContentView: View {
    var body: some View {
        VStack {
            Button(action: {
                
            }) {
                Text("Swift")
                    .modifier(MainButtonModifier())
            }
            Button(action: {
                
            }) {
                Text("Java")
                    .modifier(MainButtonModifier())
            }
            Button(action: {
                
            }) {
                Text("Python")
                    .modifier(MainButtonModifier())
            }
            Button(action: {
                
            }) {
                Text("PHP")
                    .modifier(MainButtonModifier())
            }
        }
    }
}

struct MainButtonModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
            .frame(width: 300, height: 60)
            .background(.orange)
            .foregroundColor(.white)
            .font(.title)
            .cornerRadius(30)
    }
}

かなりスッキリしました。

コード解説

まず、カスタムモディファイアは以下のように定義できます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct MainButtonModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
}
}
struct MainButtonModifier: ViewModifier { func body(content: Content) -> some View { content .padding() } }
struct MainButtonModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .padding()
    }
}

contentにどんどんモディファイアをつけていく形です。

で、使うときは、以下のように.modifier()で囲います。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Text("Swift")
.modifier(MainButtonModifier())
Text("Swift") .modifier(MainButtonModifier())
Text("Swift")
    .modifier(MainButtonModifier())

引数を持たせる

例えば、このように形は一緒だけど、背景色だけ変えたいという場合は、カスタムモディファイアに引数を持たせてあげます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct ContentView: View {
var body: some View {
VStack {
Button(action: {
}) {
Text("Swift")
.modifier(MainButtonModifier(backgroundColor: .orange))
}
Button(action: {
}) {
Text("Java")
.modifier(MainButtonModifier(backgroundColor: .blue))
}
Button(action: {
}) {
Text("Python")
.modifier(MainButtonModifier(backgroundColor: .yellow))
}
Button(action: {
}) {
Text("PHP")
.modifier(MainButtonModifier(backgroundColor: .purple))
}
}
}
}
struct MainButtonModifier: ViewModifier {
let backgroundColor: Color
func body(content: Content) -> some View {
content
.padding()
.frame(width: 300, height: 60)
.background(backgroundColor)
.foregroundColor(.white)
.font(.title)
.cornerRadius(30)
}
}
struct ContentView: View { var body: some View { VStack { Button(action: { }) { Text("Swift") .modifier(MainButtonModifier(backgroundColor: .orange)) } Button(action: { }) { Text("Java") .modifier(MainButtonModifier(backgroundColor: .blue)) } Button(action: { }) { Text("Python") .modifier(MainButtonModifier(backgroundColor: .yellow)) } Button(action: { }) { Text("PHP") .modifier(MainButtonModifier(backgroundColor: .purple)) } } } } struct MainButtonModifier: ViewModifier { let backgroundColor: Color func body(content: Content) -> some View { content .padding() .frame(width: 300, height: 60) .background(backgroundColor) .foregroundColor(.white) .font(.title) .cornerRadius(30) } }
struct ContentView: View {
    var body: some View {
        VStack {
            Button(action: {
                
            }) {
                Text("Swift")
                    .modifier(MainButtonModifier(backgroundColor: .orange))
            }
            Button(action: {
                
            }) {
                Text("Java")
                    .modifier(MainButtonModifier(backgroundColor: .blue))
            }
            Button(action: {
                
            }) {
                Text("Python")
                    .modifier(MainButtonModifier(backgroundColor: .yellow))
            }
            Button(action: {
                
            }) {
                Text("PHP")
                    .modifier(MainButtonModifier(backgroundColor: .purple))
            }
        }
    }
}

struct MainButtonModifier: ViewModifier {
    let backgroundColor: Color
    
    func body(content: Content) -> some View {
        content
            .padding()
            .frame(width: 300, height: 60)
            .background(backgroundColor)
            .foregroundColor(.white)
            .font(.title)
            .cornerRadius(30)
    }
}
コード解説

定義するときは、以下のようにletで定数を宣言しておきます。:の後には型名を記述してください。

そして、引数で受け取ったbackgroundColor.background()で囲いましょう。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct MainButtonModifier: ViewModifier {
let backgroundColor: Color
func body(content: Content) -> some View {
content
.padding()
.background(backgroundColor)
}
}
struct MainButtonModifier: ViewModifier { let backgroundColor: Color func body(content: Content) -> some View { content .padding() .background(backgroundColor) } }
struct MainButtonModifier: ViewModifier {
    let backgroundColor: Color
    
    func body(content: Content) -> some View {
        content
            .padding()
            .background(backgroundColor)
    }
}

そして、使うときに色を指定してあげます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Text("PHP")
.modifier(MainButtonModifier(backgroundColor: .purple))
Text("PHP") .modifier(MainButtonModifier(backgroundColor: .purple))
Text("PHP")
    .modifier(MainButtonModifier(backgroundColor: .purple))

Viewを定義する

カスタムモディファイアの中に、Viewを入れることも可能です。

例えば、このようにZStackで重ねてタグみたいなのをつけるときは、以下のように記述します。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
struct ContentView: View {
var body: some View {
Rectangle()
.frame(width: 200, height: 100)
.foregroundColor(.gray)
.modifier(TagModifier(text: "Swift"))
}
}
struct TagModifier: ViewModifier {
let text: String
func body(content: Content) -> some View {
ZStack(alignment: .topLeading) {
content
Text(text)
.padding(5)
.padding(.horizontal)
.font(.caption2)
.background(.orange)
.foregroundColor(.white)
}
}
}
struct ContentView: View { var body: some View { Rectangle() .frame(width: 200, height: 100) .foregroundColor(.gray) .modifier(TagModifier(text: "Swift")) } } struct TagModifier: ViewModifier { let text: String func body(content: Content) -> some View { ZStack(alignment: .topLeading) { content Text(text) .padding(5) .padding(.horizontal) .font(.caption2) .background(.orange) .foregroundColor(.white) } } }
struct ContentView: View {
    var body: some View {
        Rectangle()
            .frame(width: 200, height: 100)
            .foregroundColor(.gray)
            .modifier(TagModifier(text: "Swift"))
    }
}

struct TagModifier: ViewModifier {
    let text: String
    
    func body(content: Content) -> some View {
        ZStack(alignment: .topLeading) {
            content
            Text(text)
                .padding(5)
                .padding(.horizontal)
                .font(.caption2)
                .background(.orange)
                .foregroundColor(.white)
        }
    }
}

Rectangle()が、contentになったと思って普通に作る感じです。

評価