iOS App 程式開發

iOS App 介面設計教學:如何制作表格和集合視圖的組合介面

iOS App 介面設計教學:如何制作表格和集合視圖的組合介面
iOS App 介面設計教學:如何制作表格和集合視圖的組合介面
In: iOS App 程式開發, UI
本篇為在校大學生黃潤華先生的客座投稿,他主要使用 Swift 進行開發,有時使用 C 或者 C++ 挑戰一些算法題目。

app-介面-1

在 iPhone 和 iPad 的很多 app 裏面,我們都會看到許多用表格視圖和集合視圖制作起來的介面,大部分應該是表格視圖和集合視圖的組合介面。比如我們在 app store 裏面的遊戲欄或者 App 欄裏面會看到由表格視圖和集合視圖組合而成的漂亮介面。

App-Store裏面的UI介面

又或者,你也會在快捷指令中心MediumApple Support 裏面看到相類似的介面。

捷徑、Medium、Apple-Support裏面的UI

仔細分析不難發現這些介面的共同點,一般都是在表格視圖裏面添加集合視圖,但是呢,我們一般學習的表格視圖是用來展示一連串具有相同形式的介面的,就比如郵件裏面你收到的郵件的展示形式,或者微信裏面的聊天列表。

一般形式下的表格視圖應用

於是,我們再看一開始的那些照片,他們好像找不到甚麽相同的點,最多就是最上面的集合視圖可以找到共同點,但是集合視圖下一欄就立刻變成了表格視圖,甚至,表格視圖也有很多種形式,完全對應不起來呀,哪有甚麽共同特點?真是讓人匪夷所思。為甚麽可以實現這種介面呢?別擔心,接下來就來學學如何制作表格視圖和集合視圖並存的介面吧。

示例模型

我這個介面是模仿 Apple Support 寫的,它用三種方式來呈現數據,最開始是集合視圖,第二層是一個表格視圖,第三層也是一個表格視圖,不過這兩種表格視圖裏面的介面布局不太一樣。看完我這一篇文章以後,你甚至可以自定義更多的表格視圖和集合視圖,這完全取決於你的想法。你可以在 GitHub 上面找到完整的模型示例。

接下來我們就來學習一下吧。

制作集合視圖

我們首先來制作最開始的集合視圖,通過分析圖片,我們知道,我們需要自定義我們的集合視圖,每一個視圖裏面包含了一張照片、一個分類標簽、一個標題以及一個副標題(副標題可以沒有)。

1. 添加 TableViewController

我們先在 storyboard 裏面添加一個 TableViewController,將它設為 “is initial View Controller“。然後選擇 Editor -> Embed in -> Navigation Controller

點擊 Navigation Controller,將標題設為大標題,接著點擊 tableView Controller 上的 Navigation Item,修改其標題為探索

接著點擊 TableView 的介面,打開 Attributes inspector 介面將 content 參數修改為 Static Cells,同時將 Section 參數修改為 3

我們要使用靜態的 cell 來完成任務,第一步就是在第一個 section 裏面完成集合視圖的描繪。

打開第一個 TableView CellSize inspector,將 Row Height 修改為 230

不要忘記給你的 TableView 故事面板添加類別。我這邊用的是 TableViewController.swift 來進行 TableViewController 的操作。

2. 給 Cell 添加 View

接著從 Library 裏面拖出一個 View 控件,放在第一個 Cell 上面。

接著給 View 控件添加新的約束

3. 給 Cell 添加 Collection View

接著,從 Library 裏面拖出 Collection View 控件,將它放在 View 上面。

像剛剛 View 一樣設置相同的約束即可,同時我們要將集合視圖的滾動方向修改為水平方向。在 Attributes inspector 裏面找到 Scroll Direction,將它修改為 Horizontal

接著在 Size inspector 裏面將 Cell size 修改為 Width:290,height:185。在 Section insets 裏面修改 Left 為 15,right 為 15

最重要的一步:點擊 Cell,打開 Attributes inspector,找到 Collection Reusable View,在 Identifier 後面寫上 Cell

4. 添加照片控件

Library 裏面找到 imageView,將它拖到 Collection Cell 上面,設置它的約束。

5. 添加類別標簽與主副標題標簽

我們先來添加類別標簽。從 library 裏面拖出來一個 Label,將它拖到 imageView 上面,修改 Label字體顏色等相關參數,參考示例圖片。

同樣的,給它添加一些約束。

此時要注意:我們為了保證 label 可以隨著我們寫的字數而有彈性變化,就需要修改它距離 Cell 最右側的距離,我們將它修改為小於等於

接著是我們的主副標題。拖動兩個 label 到 imageView 的左下角,將每一個 label 的顏色修改為白色,同時也根據你的想法修改字體。當然,你也可以參考我的。

接著,我們來考慮一個小細節。如果某一個 collection cell 它沒有副標題,那麽在 iPhone 上面展示的時候,副標題那一欄是沒有字的,只有主標題有字。在美觀方面,沒有副標題的那一個 Cell,它的主標題離cell的最下方比較遠,這樣並不適合。我們希望在沒有副標題的時候,主標題可以適當地下移幾公分,這樣會顯得比較好看。如圖可以看出不同之處。

為了實現第二張圖片的功能,我們可以使用 Stack View 來完成。

按住 command 按鈕,選擇主標題 label 和副標題 label,選擇 embed in -> Stack View。

選中我們的 Stack View,打開 Attributes inspector -> View -> Content Mode -> Scale To Fill。同時將 Spacing 修改為 1。

接著我們給它添加約束,只需要添加兩個約束就可以了,位置你可以自己選擇。

到此,我們 Collection View 的介面制作就完成了。此時你的 storyboard 裏面第一個 section 的介面應該和我的一樣。

展示集合視圖的數據

下面我們來使用代碼實現集合視圖。要想實現展示數據的功能,我們需要數據源,其中所使用的圖片,你可以從 Unsplash 裏面找到。

1. 創建集合視圖數據源

新建一個 Swift 文件(command + N 即可),選擇 Swift File,點擊 Next,給它命名為 imageCollection。

打開你剛剛創建的 imageCollection.swift 文件,往裏面添加一個結構體。

import UIKit

struct imageViewCollection {
    var image: UIImage?
    var title: String = ""
    var general: String = ""
    var subTitle: String = ""
}

接著,在你的 TableViewController.swift 文件裏面寫上你的數據源:

    /**
     * 第一部分
     * 在tableViewCell裏面添加了一個collectionView,實現了4個collectionCell
     * 每一個cell裏面有一個標題,副標題,背景圖片以及分類標簽
     */

    private var images = [imageViewCollection(image: UIImage(named: "1"), title: "狗來了", general: "精選集", subTitle: ""), imageViewCollection(image: UIImage(named: "2"), title: "芒果街上的小屋", general: "晨讀",subTitle: "優美純凈的小書"), imageViewCollection(image: UIImage(named: "4"), title: "吹小號的天鵝", general: "睡前故事",subTitle: ""), imageViewCollection(image: UIImage(named: "3"), title: "時代廣場的蟋蟀", general: "下午茶",subTitle: "生命之間愛和關懷的故事")]

為了實現展示的功能,你還需要給你的 TableViewController 添加上 collection 的父類:UICollectionViewDelegate 和 UICollectionViewDataSource。

class TableViewController: UITableViewController,  UICollectionViewDelegate, UICollectionViewDataSource {

    ········

}

2. 添加 imageViewController.swift 文件

新建一個 swift 文件,File -> New -> File(或者 command + N),選擇 Cocoa Touch Class

點擊 Next,選擇以 UITableViewCell 為父類,新建類的名字為 imageViewControllerTableViewCell。

打開剛剛創建的 imageViewControllerTableViewCell.swift 文件,在裏面寫上幾個變量:

@IBOutlet weak var imageView: UIImageView! // 背景照片
@IBOutlet weak var generalLabel: UILabel!  // 類別標簽
@IBOutlet weak var titleLabel: UILabel!    // 主標題
@IBOutlet weak var subTitleLabel: UILabel! // 副標題

同時打開我們的 Main.storyboard 和 imageViewControllerTableViewCell.swift 文件,按住 control,將上述聲明的變量和故事面板裏的控件一一連線。

3. 展示我們的數據

我們還需要告訴 iPhone 我們要展示多少個集合,對此,我們在 TableViewController 裏面添加上:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return images.count
}

func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

接著,在 TableViewController.swift 文件裏面聲明一個 collectionView 變量:

@IBOutlet weak var collectionView: UICollectionView! {
    didSet {
        collectionView.backgroundColor = .clear
    }
}

和剛剛的操作一樣,將這個變量和故事面板裏的 CollectionView 連線。

為了實現展示數據的功能,我們可以像 tableView 那樣來做,在類方法 collectionView(:cellForItemAt:) 裏面進行操作:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! imageViewCollectionViewCell

        cell.imageView.image = images[indexPath.row].image
        cell.generalLabel.text = images[indexPath.row].general
        cell.titleLabel.text = images[indexPath.row].title
        cell.subTitleLabel.text = images[indexPath.row].subTitle

        if images[indexPath.row].subTitle == "" {
            cell.subTitleLabel.isHidden = true
            //cell.subTitleLabel.text = "   " // 這樣就可以看到美觀感
        }

        cell.layer.cornerRadius = 10.0
        cell.layer.masksToBounds = true

        return cell
    }

如果你現在去運行的話,你不會看到數據,我們還需要指定數據源在哪裏。在 viewDidLoad() 裏面加上兩句話:

override func viewDidLoad() {
    super.viewDidLoad()
    collectionView.delegate = self
    collectionView.dataSource = self
}

此時,你再去運行,你會發現 collectionView 可以工作了。

添加表格視圖

第二部分和第三部分其實思路是一樣的,都是為了達到 static cell 與 dynamic cell 混合使用的目的。

1. storyboard 準備工作

打開我們的故事面板,點擊一開始創建三個 Section 裏的第二個 section,將它的高度修改為 120,然後,給第二組的空白 Cell 設置 identifier 值,如圖,我將它設為 TableViewCell:

同樣的,給第三個 section 寫一個 Header,將它命名為瀏覽精選集,將它的高度修改為 60,給第三組的空白 Cell 設置 identifier 值,我將它設置為 FeaturedCell

設置第三個section

2. 創建第二部分的 Xib 文件

由於第二組和第三組的 section 需要自定義 cell,使用這裏使用 XIB 來完成。

我們先來自定義第二組的 Cell,使用 command + N 快捷鍵,創建一個 TableViewCell 文件,我將它命名為 TableViewCell.swift,注意要勾選 Also Create XIB file

打開 storyboard,點擊第二個 section 的 Cell,在右側打開 identity inspector,將它的繼承類修改為剛剛創建的 TableViewCell。

現在,我們來分析看看需要給 Cell 自定義哪一些控件。如圖所示:

打開我們的 Xib 文件,它的操作其實和 Storyboard 一樣,你只需要在 Library 裏面將需要的控件拉到 xib 文件裏面就可以了。佈局的相關知識我就不再囉嗦了,只要佈局和我的差不多就可以:

3. 添加控件代碼

接著,打開 xib 文件對應的 TableViewCell 文件,我們的是 TableViewCell.swift 文件。和上述一些步驟一樣,先在裏面聲明一些變量:

@IBOutlet weak var classLabel: UILabel!  // 類別標簽
@IBOutlet weak var titleLabel: UILabel!  // 主標題
@IBOutlet weak var subTitle: UILabel!    // 副標題
@IBOutlet weak var backgroundImage: UIImageView! {
    didSet {
        backgroundImage.layer.cornerRadius = 15.0  // 設置圓角大小
        backgroundImage.layer.masksToBounds = true
    }
} // 配對圖片

再將它們和 xib 裏面的控件一一連線即可。

接著我們需要在 storyboard 裏面使用這個控件,打開 storyboard 對應的 TableViewController.swift 文件,在裏面註冊一下 NIB,如下:

// 第二組需要自定義cell,這裏使用XIB來完成
let nib = UINib(nibName: "TableViewCell", bundle: nil)

在你的 ViewDidLoad() 裏面添加上代碼:

// 需要用代碼註冊Nib
self.tableView.register(nib, forCellReuseIdentifier: "TableViewCell")

這樣子,第二部分的佈局就差不多結束了。

4. 創建第三部分的 xib 文件

第三部分和第二部分差不多,創建方式一模一樣,只是控件的佈局不一樣。

新建一個 TableViewCell 文件,我將它命名為 FeaturedTableViewCell,勾選創建 Xib 文件的選項。

像第二個 Section 一樣,我們對它進行佈局等一系列操作:

編寫好控件的代碼:

在 TableViewController.swift 裏面註冊一下 nib:

    // 第二組需要自定義cell,這裏使用XIB來完成
    let nib = UINib(nibName: "BookTableViewCell", bundle: nil)

    // 第3組也需要自定義cell,這裏使用XIB來完成
    let featuredNib = UINib(nibName: "FeaturedTableViewCell", bundle: nil)

    override func viewDidLoad() {
        super.viewDidLoad()
        // 必須帶上這兩句話才有數據
        collectionView.delegate = self
        collectionView.dataSource = self

        // 需要用代碼註冊Nib
        self.tableView.register(nib, forCellReuseIdentifier: "TableViewCell")
        self.tableView.register(featuredNib, forCellReuseIdentifier: "FeaturedCell")

    }

非常好,我們快要接近成功了,你已經完成了 3/4 的工作了,我們還需要實現 TableView 的代理,這個可以實現 static cell 與 dynamic cell 混合使用。

展示表格視圖

這一部分是整篇文章的最後一部分,我們用代碼形式來完成數據展示的任務。

1. 創建數據源

我們先來創建第二個 section 的數據源,在 TableViewController.swift 文件裏面添上:

    /**
     * 這裏是第二部分
     * 我想要在 CollectionView 下方展示一系列書籍,使用 TableViewCell 來顯示
     * 想要在靜態的 cell 裏面實現動態的 cell 需要使用 xib 文件
     */

    // 這是我將要在 tableViewCell 裏面展示給大家的內容
    private var books = [Book(classText: "友誼", title: "塔克的郊外", subTitle: "一只蟋蟀、一只老鼠和一只貓咪的\n童話連續劇。", image: UIImage(named: "5")), Book(classText: "青春", title: "會飛的教室", subTitle: "一部由孩子們自編自演的聖誕情景劇,\n一部濃縮的校園風景錄。", image: UIImage(named: "6")), Book(classText: "浪漫", title: "銀河鐵道之夜", subTitle: "一部未完成的作品\n銀河深處,便是那幸福的天堂嗎?", image: UIImage(named: "7")), Book(classText: "生命", title: "夏洛的網", subTitle: "世界上有兩種人,一種人讀過\n《夏洛的網》,另一種人正準備讀。", image: UIImage(named: "8")), Book(classText: "冒險", title: "柳林風聲", subTitle: "一個適合坐在火爐邊,大家一起聽的\n故事。", image: UIImage(named: "9"))]

接著是第三個 section 的數據源:

    /**
     * 這是第三部分
     * 在這一部分要實現簡單的tableViewCell功能
     * 每一個 cell 由一張圖片,一個標題構成
     */
    private var featuredBooks = [Featured(image: UIImage(named: "10"), title: "小王子"), Featured(image: UIImage(named: "11"), title: "愛德華的奇妙之旅"), Featured(image: UIImage(named: "12"), title: "窗邊的小豆豆"), Featured(image: UIImage(named: "13"), title: "夢書之城"), Featured(image: UIImage(named: "14"), title: "天藍色的彼岸"), Featured(image: UIImage(named: "15"), title: "看得見風的男孩")]

2. 完善表格視圖

有了數據源以後,我們就要開始將它們寫入 tableViewCell 裏面了。說實話,操作和普通的 tableViewCell 操作一模一樣,我們只是多了一個註冊 nib 的環節。

    // 以下代理方法必須實現
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if (indexPath.section == 1) {
            let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell") as? TableViewCell
            cell?.classLabel.text = books[indexPath.row].classText
            cell?.titleLabel.text = books[indexPath.row].title
            cell?.subTitle.text = books[indexPath.row].subTitle
            cell?.backgroundImage.image = books[indexPath.row].image
            return cell!
        }

        if indexPath.section == 2 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "FeaturedCell") as? FeaturedTableViewCell
            cell?.bookImage.image = featuredBooks[indexPath.row].image
            cell?.bookTitle.text = featuredBooks[indexPath.row].title

            return cell!
        }

        return super.tableView(tableView, cellForRowAt: indexPath)
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if section == 1 {
            return books.count //這裏返回第二組的行數
        }
        if section == 2 {
            return featuredBooks.count
        }
        return super.tableView(tableView, numberOfRowsInSection: section)
    }


    override func tableView(_ tableView: UITableView, indentationLevelForRowAt indexPath: IndexPath) -> Int {
        if indexPath.section == 1 {
            return super.tableView(tableView, indentationLevelForRowAt: IndexPath(row: 0, section: 1))
        }
        if indexPath.section == 2 {
            return super.tableView(tableView, indentationLevelForRowAt: IndexPath(row: 0, section: 2))
        }
        return super.tableView(tableView, indentationLevelForRowAt: indexPath)
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if indexPath.section == 1 {
            return 120
        }
        if indexPath.section == 2 {
            return 80
        }
        return super.tableView(tableView, heightForRowAt: indexPath)
    }

我不解釋類方法的含義,這個你可以從 AppCoda 上的 Swift Book 找到你要的知識點。

讓我解釋一下那幾個判斷語句:由於我們一共有三個 section,每一個 section 對應的控件不同,就需要有一定的判斷功能,注意 section 是從 0 開始數的。

後記

這是比較簡單的模型,你可以發揮自己的想像力創造更多好看的介面。謝謝閱讀我的文章。

本篇為在校大學生黃潤華先生的客座投稿,他主要使用 Swift 進行開發,有時使用 C 或者 C++ 挑戰一些算法題目。
作者
AppCoda 編輯團隊
此文章為客座或轉載文章,由作者授權刊登,AppCoda編輯團隊編輯。有關文章詳情,請參考文首或文末的簡介。
評論
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。