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 ให้กว้างแค่ 35 point

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

เปลี่ยนมาแสดงค่าที่ปุ่มกับ Label ใหม่
เพื่อให้เราอ้างถึง ปุ่มกับ Label ได้ให้เราสร้าง Outlet เหมือนเดิมในคลาส TodoItemTableViewCell
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:) เป็นแบบนี้
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
func configure(item: TodoItem) {
titleLabel?.text = item.title
checkboxButton?.setImage(UIImage(named: item.isDone ? "check": "uncheck"), for: .normal)
}
เราจะได้โค้ดออกมาแบบนี้
import UIKit
class TodoItemTableViewCell: UITableViewCell {
@IBOutlet weak var checkboxButton: UIButton?
@IBOutlet weak var titleLabel: UILabel?
func configure(item: TodoItem) {
titleLabel?.text = item.title
checkboxButton?.setImage(UIImage(named: item.isDone ? "check": "uncheck"), for: .normal)
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
จากนั้นแก้ tableView(_:cellForRowAt:) ให้เรียก configure(item:) แบบนี้แทน
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
}
เมื่อเรารันก็ใช้ได้ละ แต่ดูเหมือน cell มันจะเล็กไปหน่อยดูเบียด ๆ กัน มาทำให้มันสูงขึ้นหน่อยดีกว่า
กลับมาที่ StackView ใน Cell ของเรา เพิ่ม Constraint Top กับ Bottom space จาก 4 เป็น 8 ดีกว่า แล้วเราจะเห็นสีแดงเกิดขึ้น นั่นคือ Constraint เรามีปัญหา

แต่พอเรารันก็ปกติดี Cell ก็สูงขึ้นด้วย นั่นเพราะเวลารัน Auto layout จะถูกคำนวณและทำให้ Cell ของเราจะเปลี่ยนขนาดสูงขึ้นเพื่อให้ได้ constraint ตามที่เรากำหนด แต่ใน storyboard ขนาดของ Cell ไม่ได้ยืดให้เราอัตโนมัติ จึงดูเหมือนว่า Constraint ที่กำหนด ไม่สามารถทำได้อย่างถูกต้อง จึงแสดง Error

เพื่อความสบายใจ ให้เราตั้งความสูงของ cell ใน storyboard เป็น 52 ใน Size Inspector แต่เวลารันมันจะไม่ได้ใช้นะ เพียงเท่านี้ Error แดง ๆ ก็จะหายไป

ในกรณที่ไม่หายลองปรับ StackView จัดของแบบ Align Center

Last updated