Xcode Libraryに自作のSwiftUIのViewとModifierを追加して生産性を上げる
はじめに
Xcode12からXcode LibraryにカスタムViewとカスタムModifierを追加できるようになりました👏
以下のようにプレビューと組み合わせることで便利に使えます。
Xcode Previewsとかよくわからないよ😭って人は、この記事をご参照ください🙇♂️
Xcode Previews入門 〜コードを書かないで画面を作る〜
実際にサンプルアプリでどのようにLibraryに追加していくか見ていきましょう!
LibraryContentProvider
Xcode LibraryにViewやModifierを追加させるためには、LibraryContentProvider
に準拠したstructを用意してあげる必要があります。
protocol LibraryContentProvider
このプロトコルにはViewとModifierで使う2つの要件があります。
struct LibraryViewContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(MyView())
}
}
struct LibraryModifierContent: LibraryContentProvider {
func modifiers(base: MyView) -> [LibraryItem] {
LibraryItem(base.myModifier(value: MyValue()))
}
}
注目すべきは、双方がLibraryItem
の配列を返すことです。
では、LibraryItemの作り方を見ていきましょう!
LibraryItemの作成方法
LibraryItemは以下のようなイニシャライザーを持ちます。
init<SnippetExpressionType>(
_ snippet: @autoclosure () -> SnippetExpressionType,
visible: Bool = true,
title: String? = nil,
category: LibraryItem.Category = .other,
matchingSignature: String? = nil
)
結構引数ありますよね、しかしどれもデフォルト引数を保つため最小の実装は以下のようにできます。
LibraryItem(
CostumView()
)
実際に使ってみる
以下のようなセルを用意しました☕️
これは、コーヒーの種類にごとの画像と説明があるセルです。
このCoffeeRowView
は今後他の画面でも使いたいので、簡単に使えるように、Xcode Libraryに追加していきたいと思います。
まず、CoffeeRowContent
というLibraryContentProvider
に準拠したstructを作ります。
struct CoffeeRowContent: LibraryContentProvider {
}
次に、今回はModifierではなくViewを追加するのでviews
を追加します💪
views
はLibraryItem
の配列です。
LibraryItem
に渡すView
は今回CoffeeRowView
を渡します。
struct CoffeeRowContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(
CoffeeRowView(coffeeType: .american)
)
}
}
これだけの実装でCoffeeRowView
がLibraryに追加されます。
以下の場面で使ってみます。
コーヒーの種類の一覧を表示したいList
です。
Xcode Library
を起動して、 CoffeeRowView
を探してみましょう。
Xcode Library
でView
やModifier
を追加するにはCanvas
を起動しておく必要があるのでご注意ください。
実際にコードにドラッグ&ドロップしてみます。
即時反映されます🎉
CoffeeType
を渡してあげることで全種類のコーヒーを表示させることができました。
(コーヒータイプの実装: https://github.com/tsuzukihashi/Coffee/blob/master/Coffee/CoffeeType.swift)
同一のViewを引数ごとにLibraryに追加する
ここでCoffeeRowViewの全貌を公開します。
struct CoffeeRowView: View {
let coffeeType: CoffeeType
let size: CGFloat = 88
@State var showFavorite: Bool = false
var body: some View {
HStack {
coffeeType.image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: size, height: size)
.background(Color.gray.opacity(0.3))
.clipShape(RoundedRectangle(cornerRadius: 16))
VStack(alignment: .leading) {
Text(coffeeType.title)
.font(.headline)
Text(coffeeType.description)
.font(.subheadline)
if showFavorite {
FavoriteView(count: coffeeType.rating)
}
}
}
}
}
ご覧の通りCoffeeRowView
はshowFavorite
というBool値がありこれを切り替えることでFavoriteView
を表示・非表示を切り替えることができるようになっています。
ですので、Xcode LibraryにもFavoriteView
を表示するLibraryItem
を先ほどと同様に追加したいと思います。
struct CoffeeRowContent: LibraryContentProvider {
@LibraryContentBuilder
var views: [LibraryItem] {
LibraryItem(
CoffeeRowView(coffeeType: .american)
)
LibraryItem(
CoffeeRowView(coffeeType: .american, showFavorite: true)
)
}
}
LibraryContentBuilder
はiOS14から使える機能で、本来ならviews
はLibraryItem
の配列として返す必要がありますが、配列として返さずとも初期化することができる便利attributesです。
以上のようにLibraryItem
を追加し、Libraryを確認しますと一つのライブラリしか見えていません。
これはLibrary
上では引数だけが異なるViewを区別することができていないからです。
ためにしにタイトルを追加してみましょう。
LibraryItem(
CoffeeRowView(coffeeType: .american),
title: "Coffee Row View"
)
LibraryItem(
CoffeeRowView(coffeeType: .american, showFavorite: true),
title: "Coffee Row View with Favorite"
)
このようにしてあげると、、、
無事二つのライブラリが追加されることが確認できました!
ModifierのLibrary対応
次にModifierをLibraryに追加していきます。
まず追加するModifierはCoffeeRowView
の画像のアスペクト比率を保ちつつ特定のサイズにするメソッドチェーンをModifierにしましょう!
まずImageを拡張してresizedToFill
というメソッドにまとめます。
extension Image {
func resizedToFill(width: CGFloat, height: CGFloat) -> some View {
self
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: width, height: height)
}
}
そうしましたら、先ほど作成したCoffeeRowContent
にmodifiers
メソッドを実装していきます。
まず、modifiers
の定義を見てみましょう。
func modifiers(base: Self.ModifierBase) -> [LibraryItem]
modifier
はViewに対して働きかけるものですので、元となるViewが必要です。
そこで元となるViewを指定することができるのが引数のbase
の箇所です。
デフォルトですとAnyView
となっておりますので、今回はImage
に対してのmodifier
なので以下のように定義しましょう。
@LibraryContentBuilder
func modifiers(base: Image) -> [LibraryItem] {
LibraryItem(
base.resizedToFill(width: 100, height: 100),
title: "resizedToFill effect"
)
}
views
のときと同様にtitle
を設定しました。
早速使ってみましょう。
Libraryを起動して、views
の隣のmodifiers
を選択してresized
と検索すると無事表示されることが確認できました!
この3行を削除して、先ほど追加したmodifier
を選択してエンターもしくはドラッグ&ドロップで適応させることができます。
coffeeType.image
// .resizable()
// .aspectRatio(contentMode: .fill)
// .frame(width: size, height: size)
.resizedToFill(width: size, height: size)
.background(Color.gray.opacity(0.3))
.clipShape(RoundedRectangle(cornerRadius: 16))
View Libraryのグループ
LibraryItem
の引数であるcategory
はView Libraryのグループを指定することができます。
デフォルトは.other
です。
LibraryItem
のcategory
にそれぞれの値をセットしてみた結果が以下のとおりです。
effect
はViewに設定してもグルーピングできていないようでした。
Modifierにセットしてみた結果は以下の通りです。
effect
を含めた全てのカテゴリが適応されていることが確認できました。
活用方法
Xcode LibraryはSPMのLibraryContetnProviderも範囲内であり、パッケージが分けられているLibraryItemも参照して使うことができます。
自分が開発しているSwiftUIでNeumorphismというデザインを簡単に表示させることができるOSSがあります。
こちらにLibraryItemを追加しました。
これをSPMで追加し、追加したプロジェクトでXcode Libraryを開くとNeumorphismUIのLibraryItemが表示されていることを確認することができます。
Canvasにドラッグ&ドロップするだけで簡単に自作のAPIを利用することができました。
LibraryItemを使いこなして爆速でUIを構築して行きたいですね!
Discussion