Custom new layout

ดูไม่ค่อยสวยเลย

เครื่องหมายติ๊กถูกอยู่ข้างหลังไม่ค่อยสวยเลย ดูแล้วนึกว่าหน้า Setting เรามาทำให้ติ๊กถูกได้โดยไม่ต้องเข้าไป Edit ข้างใน และทำให้มันดูออกว่า กดที่รายการจะเปิดหน้า Edit มาดู Detail และแก้ไขได้ แบบนี้

เริ่มจากการ custom TableViewCell มาแทน Cell แบบ Basic กันดีกว่า ให้เราสร้างไฟล์ใหม่ เลือกเป็น Cocoa Touch Class

จากนั้นตั้งชื่อเป็น TodoItemTableViewCell เป็น Subclass ของ UITableViewCell

เมื่อสร้างเสร็จ เราต้องบอก TableViewCell ใน Main.storyboard ด้วยว่าให้ใช้ class นี้

เลือกที่ todoItemCell ใน storyboard แล้วเปิด Attributes Inspector เปลี่ยน Style จาก Basic เป็น Custom จากนั้นให้เราเปิด Identity Inspector แล้วเลือก class เป็น TodoItemTableViewCell ที่เราสร้างเมื่อกี้ กด Enter

จากนั้นเราอยากให้ด้านขวาเป็นเครื่องหมายลูกศร ให้เราเปลี่ยน Accessory เป็น Disclosure Indicator

ถ้าลองรันดู จะพบว่าไม่เกิดอะไรขึ้น

TodoItemTableViewCell เป็น Subclass ของ UITableViewCell จึงทำให้โค้ดของเราที่ใช้ textLabel ใน tableView(_:cellForRowAt:) ยังใช้งานได้เหมือนเดิม แถมโค้ดเรายังเปลี่ยน Accessory เป็น Checkmark กับ None อยู่

เพิ่มรูปไอคอนติ๊กถูกอันใหม่

ก่อนที่เราจะทำปุ่มติ๊กถูกอันใหม่ เราก็ต้องเอารูปมาใส่ในโปรเจคของเราก่อน

ดาวน์โหลดรูปได้ที่นี่

ให้เราเปิด Assets.xcassets เหมือนกับตอนที่เราใส่รูปไอคอนแอป แล้วให้เราลากรูปใหม่ทั้งหมดเข้ามา เราจะได้กลุ่มของรูปขึ้นมาใหม่ 2 กลุ่มคือ check กับ uncheck

จากนั้นเรากลับไปที่ Storyboard ลากปุ่มกับ Label มาใส่ที่ Cell ของเรา แล้วใส่รูปให้ปุ่มโดยใส่ชื่อรูปที่ property image ของปุ่มใน Attributes Inspector

จากนั้นให้เราใส่ Auto layout โดยกรุ๊ปเข้าใน StackView แนวนอนก่อน ตั้ง Spacing เป็น 4 เพื่อให้ปุ่มกับ Label มันไม่ติดกัน จากนั้นตั้ง Constraint เทียบกับ container ของ Cell ทั้ง 4 ด้าน และกำหนดขอบเขตของ layout ใหม่เพื่อให้ StackView แสดงเต็ม Cell

จากนั้นเราต้องกำหนดให้ปุ่มเป็นสี่เหลี่ยมจตุรัส โดยกำหนด Constraint raio เป็น 1:1 ใส่ width ให้กว้าง 44 point

การที่ Cell เปลี่ยนขนาดตาม content ภายในจากการตั้ง Constraint เรียกว่า Self-sizing Table View Cells เดิมต้องกำหนดให้ TableView รู้ว่าเราต้องการให้ Cell ปรับขนาดเอง แต่ตั้งแต่ iOS 11 TableView จะถูกตั้งให้ทำ self-sizing โดยอัตโนมัติ

ลองรันดูใหม่ จะพบว่าปุ่มกับ Label ที่เราสร้างโผล่ขึ้นมาแล้ว

เปลี่ยนมาแสดงค่าที่ปุ่มกับ Label ใหม่

เพื่อให้เราอ้างถึง ปุ่มกับ Label ได้ให้เราสร้าง Outlet เหมือนเดิมในคลาส TodoItemTableViewCell

TodoItemTableViewCell.swift
import UIKit

class TodoItemTableViewCell: UITableViewCell {

    @IBOutlet weak var checkboxButton: UIButton?
    @IBOutlet weak var titleLabel: UILabel?

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

จากนั้นเราก็ผูกใน Storyboard เหมือนเดิม แต่คราวนี้เนื่องจากเราสร้าง Outlet ในคลาสของ TableViewCell ให้เราคลิ๊กขวาที่ TableViewCell แทนที่จะเป็น ViewController ของหน้า

แล้วผูก Outlet ทั้งปุ่มและ Label

จากนั้นให้เราแก้โค้ด tableView(_:cellForRowAt:) เป็นแบบนี้

ViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "todoItemCell", for: indexPath) as! TodoItemTableViewCell
    let item = todo.item(at: indexPath.row)
    cell.titleLabel?.text = item.title
    cell.checkboxButton?.setImage(UIImage(named: item.isDone ? "check": "uncheck"), for: .normal)
    return cell
}

เมื่อรันเราจะได้แบบนี้แล้ว

refactor หน่อย

จะเห็นว่าใน tableView(_:cellForRowAt:) เขียนโค้ด Set ค่าให้ปุ่มกับ Label เต็มเลย ให้เราย้ายไปที่ TodoItemTableViewCell ดีกว่า

เพิ่มโค้ดนี้ใน TodoItemTableViewCell

TodoItemTableViewCell.swift
func configure(item: TodoItem) {
    titleLabel?.text = item.title
    checkboxButton?.setImage(UIImage(named: item.isDone ? "check": "uncheck"), for: .normal)
}

เราจะได้โค้ดออกมาแบบนี้

TodoItemTableViewCell.swift
import UIKit

class TodoItemTableViewCell: UITableViewCell {

    @IBOutlet weak var checkboxButton: UIButton?
    @IBOutlet weak var titleLabel: UILabel?

    override func awakeFromNib() {
        super.awakeFromNib()
    }

    func configure(item: TodoItem) {
        titleLabel?.text = item.title
        checkboxButton?.setImage(UIImage(named: item.isDone ? "check": "uncheck"), for: .normal)
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

จากนั้นแก้ tableView(_:cellForRowAt:) ให้เรียก configure(item:) แบบนี้แทน

ViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "todoItemCell", for: indexPath) as! TodoItemTableViewCell
    let item = todo.item(at: indexPath.row)
    cell.configure(item: item)
    return cell
}

เมื่อเรารันก็ใช้ได้ละ

Last updated