【乾貨】僅需3 分鐘,使用 SwiftUI 開發一個新聞資訊 app(上)

原創: 彭權華
首發:「知曉雲」公衆號 - 讓你的小程序開發快人一步
在這裏插入圖片描述

SwiftUI 是蘋果最新推出的 UI 開發工具,其具有以下特點:採用聲明式語法,易於閱讀、代碼更少;跨所有蘋果平臺,共用一套
API;自動支持動態類型、暗黑模式、本地化等。採用 SwiftUI 將大大提高 UI 界面開發效率。

大家好,我是小彭。蘋果近期推出了一個全新的 SwiftUI 框架,可以極大地提高 iOS 上 UI 界面的開發效率。今天小彭就用 SwiftUI 來實現一個新聞資訊 app,看看能有多快。

【關注「知曉雲」公衆號,回覆關鍵字「SwiftUI」獲取完整代碼】

受篇幅所限,我們將通過上下兩篇文章爲大家介紹如何實現一個完整的新聞資訊 app,本篇主要內容有:
SwiftUI 的基礎知識:預覽、View 協議、修飾器、@State 特性等。
使用 NavigationView、NavigationLink、List、Text、Image 等基本視圖和 HStack、VStack 常用的佈局方式創建資訊列表頁、資訊詳情頁並顯示從 daliy.plist 文件讀取的新聞信息。

開始

在 Xcode 11,創建一個新項目,選擇 iOS->Single View APP,命名爲 Daily,並選中 Use SwiftUI。
在這裏插入圖片描述

ScenDelegate

SwiftUI 項目新增了一個 ScenDelegate.swift 文件,AppDelegate 和 ScenDelegate 共同用於管理 app 的生命週期。ScenDelegate 的 scene(_:willConnectTo:options:) 進行 UI 配置:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    let contentView = ContentView()
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
} 

預覽(Preview)

打開 ContentView.swift 文件,右上角有一個 Resume 按鈕,如果沒有看見 Resume 按鈕,選擇 Editor > Canvas。

在這裏插入圖片描述

點擊 Resume 對 ContentView 視圖進行預覽

在這裏插入圖片描述
要支持預覽視圖,需要實現 PreviewProvider 協議,並在協議屬性內返回該視圖實例,比如爲 ContentView 提供預覽:

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

以往寫一個頁面,如果這個頁面藏得很深,那麼你運行後進入到這個頁面需要耗費很長時間。現在不需要運行,通過預覽可以馬上看到頁面效果,節省了頁面開發時間。

注意:必須 macOS 10.15 以上系統才支持預覽功能!

View 協議和修飾器

ContentView 實現了 View 協議。所有視圖都需要實現 View 協議,在協議屬性 body 內提供視圖內容和佈局。

struct ContentView: View {
    var body: some View {
        Text("Hello World")        
    }
}

Text 也是一個 View。注意:自定義視圖的 body 最多創建 10 個子視圖。

通過修飾器(modifier)來配置視圖,比如將 Text 的文字設置爲紅色,字體爲 system,大小爲 16,那麼可以通過 foregroundColor、font 修飾器來配置:

Text("Hello World")
      .foregroundColor(.red)
      .font(.system(size: 16))

每個修飾器都會返回新的視圖 View,通過多個修飾器形成一個修飾鏈來完成對適配的配置。需要注意的是,修飾器的順序不同可能產生不一樣的結果。

數據準備

每一條新聞資訊包含標題、作者、發佈日期、詳細內容、圖片等信息。創建一個 swift 文件,命名爲 News.swift。在該文件內創建一個新聞模型 News:

struct News: Identifiable {
    var id: String
    var author: String
    var title: String
    var date: Date
    var content: String
    var thumbnail: String
}

爲了簡單起見,預定義了 2 條新聞資訊信息。fetchDaliy() 方法獲取新聞信息並返回 News 數組:

func fetchDaliy() -> [News] {
    
    let newsDicts = [
                ["id": "0", "title": "60 天打印一枚火箭", "author": "ifanr", "content": "今年 4 月,SpaceX 重型獵鷹完成首次商業發射", "date": "10-14"],
                ["id": "1", "title": "當你在看電視的時候,電視也在看你", "author": "ifanr", "content": "智能電視廣告的問題着實讓人心煩。", "date": "10-15"]]
    
    var newsList: [News] = []
    for newsDict in newsDicts {
        let news = News(id: newsDict["id"]!, title: newsDict["title"]!, author: newsDict["author"]!, date: newsDict["date"]!, content: newsDict["content"]!, thumbnail: newsDict["thumbnail"]!)
        newsList.append(news)
    }
    
    return newsList
}

UI 構建

App 的主要內容有:新聞列表頁、新聞詳情頁。我們將所有新聞資訊展示在一個列表上,每一行代表一條新聞,點擊一行進入到資訊詳情頁。相應地,需要三個頁面:列表頁、列表項、詳情頁。新建三個 SwiftUI 文件,分別命名爲 NewsListView.swift、NewsListRow.swift、NewsDetailView.swift 。NewListView 表示列表頁,NewsListRow 表示列表項,NewsDetailView 表示詳情頁。

頁面導航

在列表頁點擊一條新聞,將跳轉到詳情頁,同時詳情頁也能夠返回到列表頁。在 SwiftUI 中,使用 NavigationView 和 NavigationLink 來實現頁面之間的導航跳轉。將列表頁 NewsListView 嵌入到 NavigationView 中,SwiftUI 會在 NewsListView 頂部增加一個導航欄 NavigationBar。打開 ContentView.swift 文件,增加以下代碼:

struct ContentView: View {
    var body: some View {
        NavigationView {
            NewsListView()
        }.navigationViewStyle(StackNavigationViewStyle())
    }
} 

navigationViewStyle 指定導航類型,若未指定,在 iPhone 和 iPad 默認的導航類型不一樣。

新聞列表頁

在 NewsListView 中,我們需要獲取所有新聞資訊數據,並展示在一個列表上。
在這裏插入圖片描述
通過 List 聲明一個列表,並指定數據源和每一行的內容。

struct NewsListView: View {
    var newsList: [News] = fetchDaliy()
    var body: some View {
        List(newsList) { (news) in
            NavigationLink(destination: NewsDetailView()) {
                NewsListRow()
            }
        }
    }
} 

NavigationLink 通過指定目標頁面,實現頁面之間的跳轉。

另外需要注意的是,List 的初始化方法指定了數據源,數據源的元素需要實現 Identifiable,因此我們在 News 類型實現了 Identifiable。

新聞列表項

每一個列表項顯示一條新聞,內容包含圖片、標題、發佈日期。佈局如下圖:
在這裏插入圖片描述

在 SwiftUI 中,使用 HStack 和 VStack 來組合不同的使用的視圖。HStack 表示橫向關係,VStack 表示縱向關係。標題和日期是縱向關係,而標題和日期組合起來和圖片是橫向關係。NewListRow 的實現如下面代碼:

struct NewsListRow: View {
    var news: News
    var body: some View {
        HStack {
            WebImageView(imageUrl: news.thumbnail)
                .frame(width: 80, height: 100)
            VStack(alignment: .leading) {
                Text(news.title)
                    .font(.headline)
                    .padding(.top, 10)
                Spacer()
                Text(news.date)
                    .foregroundColor(.secondary)
                    .padding(.bottom, 10)
            }
            .padding(.leading, 10)
        }
    }
} 

我們需要實現 WebImageView 獲取網絡圖片並顯示,圖片未下載時顯示一個佔位圖片,當圖片下載後將顯示該網絡圖片:

struct WebImageView: View {
    @State private var uiImage: UIImage? = nil
    let placeholderImage = UIImage(named: "mine_ifanr")
    var imageUrl: String
    
    var body: some View {
        Image(uiImage: self.uiImage ?? placeholderImage!)
            .resizable()
            .onAppear(perform: downloadWebImage)
    }
    
    func downloadWebImage() {
        guard let url = URL(string: imageUrl) else { return }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let data = data, let image = UIImage(data: data) {
                self.uiImage = image
            } else {
                print("error: \(String(describing: error))")
            }
        }.resume()
    }
}

Image 是 SwiftUI 的圖片控件,uiImage 爲 nil 時,Image 視圖顯示 placeholderImage 佔位圖。當 WebImageView 出現時(onAppear),將會根據 imageUrl 下載圖片,下載完成後將 image 賦值給 uiImage,此時 SwiftUI 會自動更新 Image 的圖片。Image 能夠自動更新,是因爲將 uiImage 聲明瞭 @State 特性。將 View 的存儲屬性聲明爲 @State 後,每當該屬性的值發生變化時,SwiftUI 都會重新繪製 body 的內容。

新聞詳情頁

在新聞詳情頁,我們需要顯示標題、作者、發佈日期、詳細內容。佈局如圖:

在這裏插入圖片描述
代碼如下所示

struct NewsDetailView: View {
    var news: News
    var body: some View {
        VStack {
            Text(news.title)
                .font(.system(size: 24))
            HStack {
                Text("作者: \(news.author)")
                Text(news.date)
            }.padding(.top, 2)
            Text(news.content)
                .padding(.top, 5)
            Spacer()
        }.navigationBarTitle("詳情", displayMode: .inline)
        .padding(EdgeInsets(top: 10, leading: 10, bottom: 0, trailing: 10))
    }
}

通過 navigationBarTitle 來設置詳情的導航標題,以及展示模式。padding 設置視圖間的間隔。

小結

以上雖然解決了 UI 構建的問題,但實際應用中,數據都是動態變化的,所以我們需要與後端服務器進行交互。下一篇文章小彭將繼續爲大家介紹 SwiftUI 的 ObservableObject、@ObservedObject、@Published 等特性。以及如何從服務器獲取數據,實現新聞諮詢的動態更新。

【關注「知曉雲」公衆號,點擊菜單欄「知曉雲」-「知曉課堂」,獲取更多開發教程。】
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章