Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> สมาร์ทโฟน >> iPhone

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ในโพสต์นี้เราจะลองสร้างหน้าจอ Home ของ Spotify ใน Swift แบบโปรแกรมมิง (Programmatically) ทำไมต้องแบบโปรแกรมมิง? ผมคิดว่ารู้วิธีสร้างอย่างต่างๆ ดี และชอบเขียนโค้ดทำให้เกิดขึ้น ทักษะเหล่านี้มีประโยชน์มากหากทำงานทีม หรือใช้ Version Control

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

นี่คือหน้าจอ Home จริงของ Spotify Mobile App เพื่อให้ได้ Layout แบบนี้ เราจะใช้ UICollectionView และอาจใช้ TabBarController สร้าง Tab Navigator ด้วย

ข้อกำหนดพื้นฐาน: ตรวจสอบก่อนว่ามี Xcode 10+ และ Swift 4+ ติดตั้งแล้ว

เริ่มด้วยการสร้าง Xcode Project ใหม่:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

สิ่งแรกใน ViewController.swift เปลี่ยน Superclass เป็น UICollectionViewController แทน UIViewController เพราะ Class ของเราจะ Base บน collectionView

import UIKit
class ViewController: UICollectionViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.backgroundColor = .purple
        // Do any additional setup after loading the view.
    }
}

ถ้า Run App Build จะ Fail ต้องเพิ่มโค้ดใน AppDelegate.swift ในฟังก์ชัน didFinishLaunchingWithOptions ก่อน return:

let layout = UICollectionViewFlowLayout()
window = UIWindow()
window?.rootViewController = ViewController(collectionViewLayout: layout)

โค้ดเต็มจะเป็นแบบนี้:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let layout = UICollectionViewFlowLayout()
    window = UIWindow()
    window?.rootViewController = ViewController(collectionViewLayout: layout)
    return true
}

ตอนนี้ Run App ได้ และเห็น BackgroundColor เปลี่ยนเป็นม่วง:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ขั้นตอนต่อไป แจกแจง Layout และแบ่งพื้นที่เท่าๆ กันระหว่าง Sections

กำหนดเมธอดของ CollectionView:

  • Register Reusable Cell พร้อม Identifier ที่ Unique
  • กำหนดจำนวน Items ใน Section
  • ใช้ Cell ที่ลงทะเบียนแล้ว

ต้อง Conform UICollectionViewDelegateFlowLayout เป็น Superclass เพื่อรับ AutoComplete เมธอด เริ่ม Register CollectionViewCell

ใน viewDidLoad() เรียก collectionView.register() ลงทะเบียน Reusable Cell:

collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

กำหนดจำนวน Cell ใน collectionView ด้วย numberOfItemsInSection ตอนนี้แค่ 5 Items:

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 5
}

กำหนด Reusable Cell ด้วย cellForItemAt คืน UICollectionViewCell มี Unique ID ชื่อ cellId:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
    cell.backgroundColor = .red
    return cell
}

โค้ดเต็ม:

import UIKit
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
    let cellId: String = "cellId"
    override func viewDidLoad() {
        super.viewDidLoad()
        collectionView.backgroundColor = .purple
        collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
    }
    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
        cell.backgroundColor = .red
        return cell
    }
}

ควรเห็น 5 Items พื้นหลังสีแดง:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

เพิ่ม Width และ Height แบบกำหนดเองให้ Cells

ตอนนี้วาง Cell ให้ถูกต้องและให้ Width, Height แต่ละ Cell รับ Width ของหน้าจอเป็น Width

โชคดีที่มีเมธอด sizeForItemAt ให้กำหนด Width, Height คืนค่า CGSize:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = view.frame.width
    let height = CGFloat(200)
    return CGSize(width: width, height: height)
}

Cell รับ Width ของหน้าจอด้วย view.frame.width และ Height แบบกำหนดเป็น CGFloat

ผลลัพธ์ใน Simulator:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ดูดีแล้ว คราวนี้สร้าง Custom Cell ที่ Reusable ได้ สร้างไฟล์ Swift ใหม่ชื่อ CustomCell:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

CustomCell.swift ควรเป็นแบบนี้:

import UIKit
class CustomCell: UICollectionViewCell {
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

แก้ 2 เมธอดรองรับ Reusable Cell: collectionView.register และ cellForItemAt แก้ Register Method ก่อน แทนที่ UICollectionViewCell.self ด้วย CustomCell:

collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

Cast cellForItemAt ให้ Conform CustomCell:

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell

Run App อาจไม่เห็นเปลี่ยนแปลง ให้ CustomCell backgroundColor = .yellow ลบบรรทัด cell.backgroundColor = .red ใน cellForItemAt ควรเห็นสีเหลือง 😊

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ตอนนี้ใส่เนื้อหาลง CustomCell :D

ดู Spotify Home Screen แต่ละ Section (คือ CustomCell ของเรา) มี Section Title, Sub Cells และเลื่อนแนวนอน:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

เพิ่ม Section Title

เพิ่ม Title Label ใน Cell สร้าง titleLabel ใน Class CustomCell:

let titleLabel: UILabel = {
    let lb = UILabel()
    lb.text = "Section Title"
    lb.font = UIFont.boldSystemFont(ofSize: 14)
    lb.font = UIFont.boldSystemFont(ofSize: 14)
    return lb
}()

เพิ่ม Element ลง View ในบล็อก init():

addSubview(titleLabel)

Run App ไม่เห็นการเปลี่ยนแปลง เพราะยังไม่มี Constraint ให้ Element เพิ่ม Property lb.translatesAutoresizingMaskIntoConstraints = false ให้ titleLabel เพื่อใช้ Constraints ได้

หลังเพิ่ม titleLabel ลง View กำหนด Constraints:

addSubview(titleLabel)
titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
titleLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true

ต้องใส่ .isActive = true เสมอ ไม่งั้น Constraint ไม่ทำงาน!

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ก่อนต่อไป เปลี่ยน Background ของหน้าจอเป็นดำ และลบสีเหลืองของ Cells:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

ส่วนสำคัญ: ใส่ Sub Cells ลงในแต่ละ Cell จะเพิ่ม CollectionView ภายใน CustomCell

เพื่อเพิ่ม CollectionView ใน UICollectionViewCell ต้องเพิ่ม Properties UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource เป็น Superclass ของ CustomCell

สร้าง Element collectionView เหมือน View ธรรมดา:

let collectionView: UICollectionView = {
    // init layout
    let layout = UICollectionViewFlowLayout()
    // set direction horizontal
    layout.scrollDirection = .horizontal
    // instance of collectionView
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    // Activate constraints
    cv.translatesAutoresizingMaskIntoConstraints = false
    return cv
}()
อ้างอิง: https://stackoverflow.com/questions/44614743/add-uicollectionview-in-uicollectionviewcell

สังเกตว่าเพิ่ม layout ให้ collectionView ใน Initializer เหมือนครั้งแรกกับ viewController.swift ที่นี่ระบุทิศทาง FlowLayout เป็น .horizontal

เพิ่ม collectionView ลง View เป็น SubView ทำฟังก์ชันแยกเพื่อ Code สะอาด:

fileprivate func setupSubCells() {
    // add collectionView to view
    addSubview(collectionView)
    collectionView.dataSource = self
    collectionView.delegate = self
    // setup constraints
    // make it fit all space of CustomCell
    collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true
    collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
}

ตั้งค่า Delegate และ DataSource ให้ collectionView:

collectionView.dataSource = self

collectionView.delegate = self

เรียกฟังก์ชันในบล็อก init

Xcode จะ Error ถ้า Build เพราะยังไม่ Conform Protocols UICollectionViewDelegate และ UICollectionViewDelegateFlowLayout แก้โดย Register Sub Cell เป็น Reusable Cell

สร้างตัวแปรบนสุด Class ชื่อ cellId:

let cellId: String = "subCellID"

collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)

ขาดอีก 2 เมธอด: numberOfItemsInSection กำหนดจำนวน Cell ใน Section และ cellForItemAt กำหนด Reusable Cell เมธอดเหล่านี้จำเป็นสำห collectionView ทำงาน:

// number of cells
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 4
}
// reusable Cell
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
    cell.backgroundColor = .yellow
    return cell
}

ผลลัพธ์:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

เห็น collectionView พื้นหลังม่วง Sub Cells สีเหลือง

สิ่งสุดท้ายก่อนจบบทความ: ทำให้ subCells มี Height เท่า Section และ Width ใช้ sizeForItemAt กำหนด Height, Width:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = frame.height
    let height = frame.height
    return CGSize(width: width, height: height)
}

เสร็จแล้ว 😎:

สร้างแอป iOS แบบ Spotify ด้วย Swift และ AutoLayout แบบโปรแกรมมิง

เยี่ยม! หยุดที่นี่ก่อน เพราะไม่อยากยาวเกิน ไป ภาค 2 จะเพิ่มรูปภาพจำลองและเติมข้อมูล

Full Source Code 👇

ถ้ามีเพิ่มเติม คำถาม หรือแก้ไข Comment ได้ด้านล่าง 👇 หรือแท็กผมที่ Twitter

Subscribe อีเมลเพื่อรับแจ้งตอนเผยแพร่ภาค 2 ของบทเรียนนี้