苦等半年的 Swift Playgrounds 4.0 終於發佈,新版更新了非常多功能,特別是在非同步(async)的相關指令,非同步模式是網路程式、GUI互動、並行運算的基礎,而並行運算又是大數據、人工智慧、3D繪圖等高速運算所必須,因此顯得特別重要,而且新語法能簡化程式碼的撰寫,本單元後半部分(第6~10課)會盡量採用新的指令語法,須配合使用 Swift Playgrounds 4.0。

下載 Swift Playgrounds 4.0 之前,記得要將作業系統升級到 iPadOS 15.2 或 macOS 12.1,Swift Playgrounds下載入口: https://www.apple.com/tw/swift/playgrounds/

image.png

第2課我們學習從網路抓圖,範例3-2a是一個非常基本的網路抓圖程式:

// 3-2a 讀取網路圖片(URLSession)
// Created by Heman, 2021/09/02
import PlaygroundSupport
import SwiftUI

let 網址 = "<https://picsum.photos/720/1280>"

struct 抓圖: View {
    @State var 下載圖片: UIImage?
    var body: some View {
        if 下載圖片 == nil {
            ProgressView()
                .onAppear {
                    guard let myURL = URL(string: 網址) else { return }
                    URLSession.shared.dataTask(with: myURL) { 回傳資料, 回應碼, 錯誤碼 in
                        if let 圖檔 = UIImage(data: 回傳資料!) {
                            print(回應碼 ?? "No response")
                            下載圖片 = 圖檔
                        } else {
                            print(錯誤碼 ?? "No error")
                        }
                    }.resume()
                }
        } else {
            Image(uiImage: 下載圖片!)
                .resizable()
                .scaledToFit()
        }
    }
}

PlaygroundPage.current.setLiveView(抓圖())

原來的抓圖程式,主要是用 URLSession.shared.dataTask() 這個通用物件,大部分網路程式都可以用它來寫,是本單元前半部(第1課到第5課)的核心物件。

Swift Playgrounds 4.0 增加了一個 AsyncImage 物件,讓我們寫程式下載網路圖片變得更容易,將上面範例3-2a改用 AsyncImage 後,程式簡化如下,減少了將近一半的程式碼,不可思議吧:

// 3-6a 讀取網路圖片(AsyncImage)
// Modified (based on 3-2a) by Heman, 2021/12/16
import PlaygroundSupport
import SwiftUI

let 網址 = "<https://picsum.photos/720/1280>"

struct 抓圖: View {
    var body: some View {
        AsyncImage(url: URL(string: 網址)) { 下載圖片 in
            下載圖片
                .resizable()
                .scaledToFit()
        } placeholder: {
            ProgressView()
        }
    }
}

PlaygroundPage.current.setLiveView(抓圖())

AsyncImage 也是一個 View 物件類型,跟其他 SwiftUI 的 View 物件語法類似,所以用起來很方便。物件的初始化需要一個 URL 物件實例當作參數,下載取得的圖片會傳入後面接的匿名函式,在取得下載圖片之前,可指定另一個 View 當作緩衝(在此為 ProgressView()),這個緩衝的參數稱為 placeholder,就是臨時用來佔位置的東西(圖片或視圖)。

因為 AsyncImage 是非同步的運作模式,所以運作過程跟原來範例 3-2a 完全相同,會先顯示 ProgressView() 視圖,當下載完成(取得 Response)後,才會進入後接的匿名函式,顯示已下載的圖片。

注意原來3-2a裡用來更新畫面的狀態變數(@State var)已不需要,表示 AsyncImage 能自己掌握狀態更新畫面。

執行結果如下:

image.png

下一節:3-6b 阿貓阿狗(AsyncImage phase)