はじめに
iOS14以前はSwiftUIでリンク付きテキストを実装しようとすると、NSAttributedTextやUILabelなどを使う必要があり、結構面倒でした。
iOS15以降はめちゃめちゃ簡単に実装することができます。
Textでリンク付きテキストを実装する
Textはマークダウンを解釈できるので、以下のように実装するだけでリンク付きテキストを実装できます。
Text("[リンク](https://example.com)だよ")
しかし、例えばURL部分に変数やメソッドを使用するとリンクになりません。
let url = "https://example.com"
Text("[リンク](\(url))だよ")
変数やメソッドを使用する場合は、以下のように実装しましょう。
let url = "https://example.com"
Text(.init("[リンク](\(url))だよ"))
Textのイニシャライザについて補足
どうして変数を使用するとリンクが解釈されないのでしょうか?
それを理解するにはSwiftUI.Textのイニシャライザの挙動について知る必要があります。
以下のように文字列リテラルを使用した場合、init(_:tableName:bundle:comment:)が使用されます。
そして、このイニシャライザでは文字列にマークダウンが含まれている場合、テキストをスタイリングして表示するようになっています。
Text("[リンク](https://example.com)だよ")
一方、変数あるいは変数を含む文字列を使用した場合、init(_:)が使用されます。
[リンク](https://example.com)だよ
という表示にはならないのでマークダウンを解釈してないわけではない?のかもしれませんが、スタイリングはされません。
Text("[リンク](\(url))だよ")
以下のように実装した場合、Textに対して明示的にinit(_:tableName:bundle:comment:)
を使用することを指定しているため、スタイリングされるというわけです。
Text(.init("[リンク](\(url))だよ"))
...ちなみに、型を明示的に書くとこうなるはずなんですが、これだとスタイリングされませんでした。
Text(LocalizedStringKey("[リンク](\(url3))だよ"))
代わりにこうするとスタイリングされました。
Text(LocalizedStringKey(stringLiteral: "[リンク](\(url3))だよ"))
いまいち納得がいきませんが、、分かる方いたら教えてほしいです
リンクタップを検知して何かする
リンクなので、タップされたら何かアクションをさせたいですよね。
これはenvironment(_:_:)
モディファイアを使用すると実現できます。
VStack(spacing: 16) {
Text("[リンク](https://example.com)だよ")
Text(.init("[リンク](\(url))だよ"))
}
.environment(\.openURL, OpenURLAction { url in
print(url) // タップされたURLを出力する
return .handled
})
※environment(_:_:)
モディファイアの説明はここでは割愛します
Swift Playgroundで試す
以下のコードをPlaygroundに貼って試してみてください。
import UIKit
import SwiftUI
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let url2 = "https://example2.com"
let url3 = "https://example3.com"
struct ContentView: View {
var body: some View {
VStack(spacing: 16) {
Text("[リンク](https://example1.com)だよ")
Text("[リンク](\(url2))だよ")
Text(.init("[リンク](\(url3))だよ"))
}
.frame(width: 200, height: 200)
.environment(\.openURL, OpenURLAction { url in
print(url)
return .handled
})
}
}
PlaygroundPage.current.setLiveView(ContentView())