21
9

More than 1 year has passed since last update.

【iOS15.0+】SwiftUI.Textでリンク付きテキストを実装する

Last updated at Posted at 2023-07-15

はじめに

iOS14以前はSwiftUIでリンク付きテキストを実装しようとすると、NSAttributedTextやUILabelなどを使う必要があり、結構面倒でした。

iOS15以降はめちゃめちゃ簡単に実装することができます。

Textでリンク付きテキストを実装する

Textはマークダウンを解釈できるので、以下のように実装するだけでリンク付きテキストを実装できます。

Text("[リンク](https://example.com)だよ")

image.png

しかし、例えばURL部分に変数やメソッドを使用するとリンクになりません。

let url = "https://example.com"
Text("[リンク](\(url))だよ")

image.png

変数やメソッドを使用する場合は、以下のように実装しましょう。

let url = "https://example.com"
Text(.init("[リンク](\(url))だよ"))

image.png

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))だよ"))

いまいち納得がいきませんが、、分かる方いたら教えてほしいです :pray:

リンクタップを検知して何かする

リンクなので、タップされたら何かアクションをさせたいですよね。
これは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())

text-link.gif

21
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
9