SwiftUIのImageは Image(“chincoteague”)と書けばプロジェクト内「Assets.xcassets」に配置した“chincoteague”画像を利用することができます。
しかし、Assetsに配置した場合インターネットなどから 動的にアプリ内画像変更などができません。そういった場合に対処するため、Webサーバーに配置された画像をアプリ内のImageで表示させる方法です。
Imageにはinit(uiImage: UIImage)がありますが、ここにWebサーバーから取得した画像データを埋めれば実現できます。Webサーバーのデータ取得方法は前回SwiftUIからWebサーバー文字列を取得する方法を書いていますが、この方法と同じくURLSessionを使います。
URLSession.shared.dataTask(with: URL(string: "https://mojeld.com/img/charleyrivers.jpg")!) { d1, _, _ in }
dataTask()にURLを入れると、{ d1, _, _ in }の中のd1にデータが入ります。取得後UIImage()にd1を設定すれば画像データがUIImage()に入ります。
let d2 = UIImage(data: d1)
Webサーバーからの画像取得からUIImageに設定するまでの動作をObservableObject継承したクラスにまとめます。
class TURLImage: ObservableObject { enum TNetStatus { case none; case standby; case successfll } @Published var netStatus: TNetStatus = .none @Published var img : UIImage = UIImage() func getImg(_ urlStr: String) -> Void { guard let url1 = URL(string: urlStr) else { return } netStatus = .standby URLSession.shared.dataTask(with: url1) { data, _, _ in guard let d1 = data, let d2 = UIImage(data: d1, scale: 5) else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 1 ) { self.netStatus = .successfll self.img = d2 } }.resume() } }
TURLImageという名称のクラスを作成しました。そこにはTNetStatusというステータスを示すenumとUIImageを@Publishedに置いています。@Publishedに配置した場合View側でクラス内の変更を知ることができます。配置したUIImageのscaleを「5」に設定しています。これたぶんscale 1/5ということだと思います。クラス内のvar img : UIImageの更新は、DispatchQueue.main.asyncAfter()を使って1秒間待ってから処理しています。これは読込み中描画を1秒以上表示したかったからです。
つぎは、View側の処理です。iOSアプリ画面のボタンを押すと「読み込み中…」を表示しWebサーバーからデータを取得するまでのコードが下記です。
struct TNetImage: View { @State var uStr: String @ObservedObject var urlImage: TURLImage = TURLImage() var body: some View { VStack { if ( self.urlImage.netStatus == TURLImage.TNetStatus.successfll ) { Image(uiImage: self.urlImage.img) } else if ( self.urlImage.netStatus == TURLImage.TNetStatus.standby) { Text("読み込み中…") } Button("Button1", action: { self.urlImage.getImg(self.uStr) } ) Spacer() } } }
TNetImageというViewを作成し、 「Button1」を押したあと「読み込み中…」が表示されWebサーバーからの画像データ取得完了後Image表示に切り替わります。