実現するもの
全体のイメージ
詳しく見ましょう
speciesData.json
"detailText": [
"コガタペンギンは保護色があり、青い背中は上から見れば海と同じに見え、白い腹は下の捕食者と獲物を迷惑させることができます。したがって、夜に波から上陸するとき、海岸に打ち寄せる白い波が突然立ち上がって、コガタペンギンの群れになったように見えます。",
"これはまさにおとぎ話のようで、英語では「フェアリーペンギン(Fairy Penguin)」とも呼ばれています。",
"<image>02</image>",
"<caption>夜に帰ってくるコガタペンキンの群れ。画像:Phillipislandtourism / Wikimedia Commons</caption>",
"コガタペンギンの平均寿命は6〜7年くらいですが、飼育下では20年を超えることもあります。",
]
上記のコードで生成されたのはこちら⬇️
このように、**文字列(String)**だけのデータをもとに、
コンテンツ | 記述 |
---|---|
本文(Text) | 本文 |
画像(Image) | <image>画像番号</image> |
画像の説明文(Text) | <caption>説明文</caption> |
が混在しているマルチメディアのコンテンツをネイティブUIで自動生成します。 |
ここは例示なので、コンテンツの種類と記述方法は簡単にカスタマイズできます。
実装方法
正規表現で記述を認識
正規表現に詳しくなくても大丈夫です。
まず、ある文字列が指定したパターンと一致しているか、を教えてくれるものとして覚えましょう。
例えば、"<image>"で始まり、"</image>"で終わる文字列であるか、ということを判断してくれます。
パターンの定義
RegularExpression.swift
enum RegexPattern: String {
case image = "^<image>.*</image>$"
case caption = "^<caption>.*</caption>$"
}
"^<image>.*</image>$"というパターンは、
"<image>"で始まり、"</image>"で終わることを意味しています。
この部分を書き換えれば、記述方法をカスタマイズできます。
一致判断とタグ削除
RegularExpression.swift
import Foundation
extension String {
//正規表現の一致判断
func match(pattern:RegexPattern) -> Bool {
let pattern = pattern.rawValue
let regex = try! NSRegularExpression(pattern:pattern)
return regex.firstMatch(in:self, range:NSRange(self.startIndex..., in:self)) != nil
}
//両端のタグの削除
func strip() -> String {
let lstrip = self.replacingOccurrences(of:"^<\\w+>", with:"", options:NSString.CompareOptions.regularExpression, range:self.range(of:self))
return lstrip.replacingOccurrences(of:"</\\w+>$", with:"", options:NSString.CompareOptions.regularExpression, range:lstrip.range(of:lstrip))
}
}
ここでは、呼び出しやすくするために、String型のメソッドとして実装します。
ネイティブUIを生成
SpeciesDetail.swift
import SwiftUI
struct SpeciesDetail: View {
var species: Species
var body: some View {
ScrollView {
VStack {
//文字列ごと処理
ForEach(self.species.detailText, id: \.self) { text -> AnyView in
//imageのパターンと一致した場合
if text.match(pattern: .image) {
return AnyView(
//SwiftUIのImageを利用し、画像コンテンツの表示をカスタマイズ
Image("\(self.species.imageName)_\(text.strip())")
.resizable()
.scaledToFit()
)
//captionのパターンと一致した場合
} else if text.match(pattern: .caption){
return AnyView(
//SwiftUIのTextを利用し、画像の説明文の表示をカスタマイズ
Text(text.strip())
.foregroundColor(Color.gray)
.font(.custom("Baskerville", size: 16))
)
//どのパターンとも一致しない場合
} else {
return AnyView(
//SwiftUIのTextを利用し、本文の表示をカスタマイズ
Text(" " + text)
.padding(.top, 18)
.font(.custom("Baskerville", size: 20))
.lineSpacing(12)
.fixedSize(horizontal: false, vertical: true)
)
}
}
}
.padding()
}
}
}
struct SpeciesDetail_Previews: PreviewProvider {
static var previews: some View {
SpeciesDetail(species: speciesData[0])
}
}
フルコード
以上は自分のアプリの一部として実装しています。
詳しくはこちら(GitHubへ)をご覧ください。