ตัวอย่างคลาสย่อย UIView ที่กำหนดเอง
ฉันมักจะสร้างแอป iOS โดยไม่ใช้สตอรี่บอร์ดหรือปลายปากกา ฉันจะแบ่งปันเทคนิคบางอย่างที่ฉันได้เรียนรู้เพื่อตอบคำถามของคุณ
การซ่อนinit
วิธีการที่ไม่ต้องการ
คำแนะนำแรกของฉันคือการประกาศฐานUIView
เพื่อซ่อนตัวเริ่มต้นที่ไม่ต้องการ ผมได้กล่าวถึงวิธีการนี้ในรายละเอียดในคำตอบของฉัน "วิธีการซ่อน Storyboard และปลายปากกา Initializers เฉพาะใน UI Subclasses" หมายเหตุ: วิธีนี้ถือว่าคุณจะไม่ใช้BaseView
หรือลูกหลานของมันในสตอรีบอร์ดหรือไส้ปากกาเนื่องจากจะทำให้แอปหยุดทำงานโดยเจตนา
class BaseView: UIView {
init() {
super.init(frame: CGRect.zero)
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
คุณ subclass UIView BaseView
ที่กำหนดเองควรสืบทอดจาก ต้องเรียก super.init () ใน initializer init(coder:)
มันไม่จำเป็นต้องใช้ นี่แสดงให้เห็นในตัวอย่างด้านล่าง
การเพิ่ม UITextField
ฉันสร้างคุณสมบัติที่จัดเก็บไว้สำหรับมุมมองย่อยที่อ้างอิงนอกinit
เมธอด โดยทั่วไปฉันจะทำเช่นนั้นสำหรับ UITextField ฉันชอบที่จะ subviews instantiate ภายในประกาศของทรัพย์สิน subview let textField = UITextField()
เช่นนี้:
UITextField จะมองไม่เห็นจนกว่าคุณจะเพิ่มไปที่รายการ subview addSubview(_:)
มุมมองที่กำหนดเองโดยการเรียก นี่แสดงให้เห็นในตัวอย่างด้านล่าง
เค้าโครงแบบเป็นโปรแกรมที่ไม่มีเค้าโครงอัตโนมัติ
UITextField จะไม่สามารถมองเห็นได้เว้นแต่คุณจะกำหนดขนาดและตำแหน่ง ฉันมักจะทำรูปแบบในรหัส (ไม่ได้ใช้เค้าโครงอัตโนมัติ) ภายในวิธี layoutSubviews layoutSubviews()
ถูกเรียกในตอนแรกและเมื่อใดก็ตามที่มีเหตุการณ์การปรับขนาดเกิดขึ้น สิ่งนี้ช่วยให้สามารถปรับเค้าโครงได้ขึ้นอยู่กับขนาดของ CustomView ตัวอย่างเช่นหาก CustomView ปรากฏเต็มความกว้างบน iPhone และ iPad ขนาดต่างๆและปรับให้หมุนได้จำเป็นต้องรองรับขนาดเริ่มต้นจำนวนมากและปรับขนาดแบบไดนามิก
คุณสามารถอ้างถึงframe.height
และframe.width
ภายในlayoutSubviews()
เพื่อรับมิติข้อมูลของ CustomView สำหรับการอ้างอิง นี่แสดงให้เห็นในตัวอย่างด้านล่าง
ตัวอย่าง UIView Subclass
UIView กำหนดเอง subclass มี UITextField init?(coder:)
ซึ่งไม่จำเป็นต้องใช้
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
เค้าโครงแบบเป็นโปรแกรมพร้อมเค้าโครงอัตโนมัติ
คุณยังสามารถใช้เลย์เอาต์โดยใช้การจัดวางอัตโนมัติในโค้ด เนื่องจากฉันไม่ได้ทำแบบนี้บ่อยๆฉันจะไม่แสดงตัวอย่าง คุณสามารถดูตัวอย่างการใช้งาน Auto Layout ในโค้ดบน Stack Overflow และที่อื่น ๆ บนอินเทอร์เน็ต
กรอบรูปแบบโปรแกรม
มีเฟรมเวิร์กโอเพนซอร์สที่ใช้เลย์เอาต์ในโค้ด หนึ่งฉันสนใจใน แต่ไม่ได้พยายามเป็นLayoutKit ซึ่งเขียนโดยทีมพัฒนา LinkedIn จากที่เก็บ Github: "LinkedIn สร้าง LayoutKit เนื่องจากเราพบว่า Auto Layout มีประสิทธิภาพไม่เพียงพอสำหรับลำดับชั้นของมุมมองที่ซับซ้อนในมุมมองที่เลื่อนได้"
ทำไมใส่fatalError
ในinit(coder:)
เมื่อสร้างคลาสย่อย UIView ที่จะไม่ใช้ในสตอรีบอร์ดหรือปลายปากกาคุณอาจแนะนำตัวเริ่มต้นที่มีพารามิเตอร์และข้อกำหนดการเริ่มต้นที่แตกต่างกันซึ่งไม่สามารถเรียกใช้โดยinit(coder:)
วิธีการ หากคุณไม่ได้ทำการ init (coder :) ด้วย a fatalError
อาจทำให้เกิดปัญหาที่สับสนมากในบรรทัดหากใช้ใน storyboard / nib โดยไม่ได้ตั้งใจ fatalError ยืนยันเจตนาเหล่านี้
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
หากคุณต้องการเรียกใช้โค้ดบางอย่างเมื่อคลาสย่อยถูกสร้างขึ้นโดยไม่คำนึงว่าจะสร้างในโค้ดหรือสตอรี่บอร์ด / ปลายปากกาคุณสามารถทำสิ่งต่อไปนี้ได้ (ตามคำตอบของ Jeff Gu Kang )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
}
}