UIView
ฉันมีตัวชี้ไปที่ ฉันจะเข้าถึงได้UIViewController
อย่างไร [self superview]
เป็นอีกสิ่งหนึ่งUIView
แต่ไม่ใช่UIViewController
ใช่ไหม
UIView
ฉันมีตัวชี้ไปที่ ฉันจะเข้าถึงได้UIViewController
อย่างไร [self superview]
เป็นอีกสิ่งหนึ่งUIView
แต่ไม่ใช่UIViewController
ใช่ไหม
คำตอบ:
ใช่superview
เป็นมุมมองที่มีมุมมองของคุณ มุมมองของคุณไม่ควรรู้ว่าตัวควบคุมมุมมองของมันคืออะไรเพราะจะทำลายหลักการ MVC
ในทางกลับกันคอนโทรลเลอร์จะรับรู้ว่ามุมมองใดที่รับผิดชอบ ( self.view = myView
) และโดยปกติมุมมองนี้จะมอบหมายวิธี / เหตุการณ์สำหรับการจัดการกับคอนโทรลเลอร์
โดยปกติแทนที่จะเป็นตัวชี้ไปยังมุมมองของคุณคุณควรมีตัวชี้ไปยังตัวควบคุมของคุณซึ่งในทางกลับกันจะสามารถดำเนินการควบคุมตรรกะบางอย่างหรือส่งบางสิ่งบางอย่างไปยังมุมมอง
จากUIResponder
เอกสารสำหรับnextResponder
:
คลาส UIResponder ไม่ได้จัดเก็บหรือตั้งค่าการตอบกลับโดยอัตโนมัติต่อไปแทนที่จะส่งค่าศูนย์เป็นค่าเริ่มต้น คลาสย่อยต้องแทนที่วิธีนี้เพื่อตั้งค่าการตอบกลับครั้งต่อไป UIView ใช้วิธีนี้โดยการกลับวัตถุ UIViewController ที่จัดการมัน (ถ้ามีหนึ่ง) หรือ SuperView (ถ้ามันไม่ได้) ; UIViewController ใช้วิธีนี้โดยส่งคืน superview ของมุมมอง UIWindow ส่งคืนวัตถุแอปพลิเคชันและ UIApplication ส่งคืนศูนย์
ดังนั้นหากคุณเรียกคืนมุมมองnextResponder
จนกว่าจะเป็นประเภทUIViewController
นั้นคุณจะมี parent viewController
โปรดทราบว่ามันยังอาจไม่มีตัวควบคุมมุมมองหลัก แต่ถ้ามุมมองไม่ได้เป็นส่วนหนึ่งของลำดับชั้นการดูของ viewController
ส่วนขยายSwift 3 และSwift 4.1 :
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder?.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
ส่วนขยาย Swift 2:
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.nextResponder()
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
หมวดหมู่ Objective-C:
@interface UIView (mxcl)
- (UIViewController *)parentViewController;
@end
@implementation UIView (mxcl)
- (UIViewController *)parentViewController {
UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
responder = [responder nextResponder];
return (UIViewController *)responder;
}
@end
มาโครนี้หลีกเลี่ยงมลภาวะประเภท:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
;) โพสต์ที่แก้ไขมาก
@คำตอบในหนึ่งบรรทัด (ทดสอบในSwift 4.1 ):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
การใช้งาน:
let vc: UIViewController = view.parentViewController
parentViewController
ไม่สามารถกำหนดได้public
หากส่วนขยายอยู่ในไฟล์เดียวกันกับที่UIView
คุณสามารถตั้งค่าfileprivate
มันรวบรวม แต่มันใช้งานไม่ได้! 😐
สำหรับการดีบักเท่านั้นคุณสามารถเรียก_viewDelegate
ใช้มุมมองเพื่อรับตัวควบคุมมุมมองได้ นี่เป็น API ส่วนตัวดังนั้นไม่ปลอดภัยสำหรับ App Store แต่สำหรับการแก้ไขข้อบกพร่องจะมีประโยชน์
วิธีการที่มีประโยชน์อื่น ๆ :
_viewControllerForAncestor
รับตัวควบคุมแรกที่จัดการมุมมองในห่วงโซ่ superview (ขอบคุณ n00neimp0rtant)_rootAncestorViewController
- รับตัวควบคุมบรรพบุรุษซึ่งมีมุมมองลำดับชั้นถูกตั้งค่าในหน้าต่างปัจจุบัน_viewControllerForAncestor
จะเข้าไปสำรวจตัวอย่างก่อนจนกว่าจะพบคนแรกที่เป็นของตัวควบคุมมุมมอง
ในการรับการอ้างอิงถึง UIViewController ที่มี UIView คุณสามารถขยาย UIResponder (ซึ่งเป็น super class สำหรับ UIView และ UIViewController) ซึ่งอนุญาตให้ขึ้นผ่านห่วงโซ่การตอบกลับและเข้าถึง UIViewController (มิเช่นนั้นจะส่งคืนศูนย์)
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.nextResponder() is UIViewController {
return self.nextResponder() as? UIViewController
} else {
if self.nextResponder() != nil {
return (self.nextResponder()!).getParentViewController()
}
else {return nil}
}
}
}
//Swift 3
extension UIResponder {
func getParentViewController() -> UIViewController? {
if self.next is UIViewController {
return self.next as? UIViewController
} else {
if self.next != nil {
return (self.next!).getParentViewController()
}
else {return nil}
}
}
}
let vc = UIViewController()
let view = UIView()
vc.view.addSubview(view)
view.getParentViewController() //provide reference to vc
วิธีที่รวดเร็วและทั่วไปใน Swift 3:
extension UIResponder {
func parentController<T: UIViewController>(of type: T.Type) -> T? {
guard let next = self.next else {
return nil
}
return (next as? T) ?? next.parentController(of: T.self)
}
}
//Use:
class MyView: UIView {
...
let parentController = self.parentController(of: MyViewController.self)
}
หากคุณไม่คุ้นเคยกับรหัสและคุณต้องการค้นหา ViewController ที่สอดคล้องกับมุมมองที่กำหนดคุณสามารถลอง:
po (UIView *) 0x7fe523bd3000 po [(UIView *) 0x7fe523bd3000 หน้าถัดไปคำตอบ] po [[(UIView *) 0x7fe523bd3000 ถัดไปตอบกลับ] ถัดไปตอบกลับ] po [[[(UIView *) 0x7fe523bd3000 ถัดไปตอบกลับ] ถัดไปตอบกลับ] ถัดไปตอบกลับ] ...
ในกรณีส่วนใหญ่คุณจะได้รับ UIView แต่ในบางครั้งจะมีคลาสที่ใช้ UIViewController
ฉันคิดว่าคุณสามารถเผยแพร่การแตะไปที่ตัวควบคุมมุมมองและปล่อยให้จัดการได้ นี่เป็นวิธีการที่ยอมรับได้มากกว่า สำหรับการเข้าถึงตัวควบคุมมุมมองจากมุมมองคุณควรรักษาการอ้างอิงไปยังตัวควบคุมมุมมองเนื่องจากไม่มีวิธีอื่น ดูกระทู้นี้มันอาจช่วย: การเข้าถึงตัวควบคุมมุมมองจากมุมมอง
รหัสปลอดภัยเพิ่มเติมสำหรับ Swift 3.0
extension UIResponder {
func owningViewController() -> UIViewController? {
var nextResponser = self
while let next = nextResponser.next {
nextResponser = next
if let vc = nextResponser as? UIViewController {
return vc
}
}
return nil
}
}
อนิจจาสิ่งนี้เป็นไปไม่ได้เว้นแต่คุณจะ subclass มุมมองและจัดให้มีคุณสมบัติอินสแตนซ์หรือเหมือนกันซึ่งเก็บการอ้างอิงตัวควบคุมมุมมองภายในเมื่อมีการเพิ่มมุมมองในฉาก ...
ในกรณีส่วนใหญ่ - มันง่ายมากที่จะแก้ไขปัญหาดั้งเดิมของโพสต์นี้เนื่องจากคอนโทรลเลอร์ส่วนใหญ่เป็นที่รู้จักกันดีในกลุ่มโปรแกรมเมอร์ซึ่งรับผิดชอบในการเพิ่มมุมมองย่อยลงใน ViewController's ;-) ซึ่งเป็นเหตุผลที่ฉันเดาว่า Apple ไม่เคยใส่ใจที่จะเพิ่มคุณสมบัตินั้น
สายไปหน่อย แต่นี่เป็นส่วนขยายที่ช่วยให้คุณค้นหาผู้ตอบคำถามทุกประเภทรวมถึง ViewController
extension NSObject{
func findNext(type: AnyClass) -> Any{
var resp = self as! UIResponder
while !resp.isKind(of: type.self) && resp.next != nil
{
resp = resp.next!
}
return resp
}
}
หากคุณตั้งค่าเบรกพอยต์คุณสามารถวางสิ่งนี้ลงในดีบักเกอร์เพื่อพิมพ์ลำดับชั้นมุมมอง:
po [[UIWindow keyWindow] recursiveDescription]
คุณควรจะสามารถพบผู้ปกครองของมุมมองของคุณที่ใดที่หนึ่งในระเบียบ :)
recursiveDescription
พิมพ์ลำดับชั้นมุมมองเท่านั้นไม่ใช่มุมมองคอนโทรลเลอร์