Tôi có một con trỏ đến a UIView
. Làm thế nào để tôi truy cập nó UIViewController
? [self superview]
là một UIView
, nhưng không phải UIViewController
, phải không?
Tôi có một con trỏ đến a UIView
. Làm thế nào để tôi truy cập nó UIViewController
? [self superview]
là một UIView
, nhưng không phải UIViewController
, phải không?
Câu trả lời:
Vâng, đó superview
là chế độ xem chứa quan điểm của bạn. Khung nhìn của bạn không nên biết chính xác bộ điều khiển khung nhìn của nó là gì, bởi vì điều đó sẽ phá vỡ các nguyên tắc MVC.
Mặt khác, bộ điều khiển biết khung nhìn nào chịu trách nhiệm cho ( self.view = myView
) và thông thường, khung nhìn này ủy nhiệm các phương thức / sự kiện để xử lý cho bộ điều khiển.
Thông thường, thay vì một con trỏ tới chế độ xem của bạn, bạn nên có một con trỏ tới bộ điều khiển của mình, điều này có thể thực hiện một số logic điều khiển hoặc chuyển một cái gì đó cho chế độ xem của nó.
Từ UIResponder
tài liệu cho nextResponder
:
Lớp UIResponder không tự động lưu trữ hoặc thiết lập phản hồi tiếp theo, thay vào đó trả về nil theo mặc định. Các lớp con phải ghi đè phương thức này để đặt phản hồi tiếp theo. UIView thực hiện phương thức này bằng cách trả về đối tượng UIViewControll quản lý nó (nếu nó có) hoặc giám sát của nó (nếu không) ; UIViewControll thực hiện phương thức bằng cách trả về giám sát của khung nhìn; UIWindow trả về đối tượng ứng dụng và UIApplication trả về nil.
Vì vậy, nếu bạn lặp lại một khung nhìn nextResponder
cho đến khi nó thuộc kiểu UIViewController
, thì bạn có bất kỳ khung nhìn cha mẹ nào của khung nhìn.
Lưu ý rằng nó vẫn có thể không có bộ điều khiển xem cha. Nhưng chỉ khi chế độ xem không phải là một phần của hệ thống phân cấp chế độ xem của chế độ xem.
Phần mở rộng Swift 3 và 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
}
}
Tiện ích mở rộng 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
}
}
Mục tiêu-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
Macro này tránh ô nhiễm thể loại:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
UIResponder
;). Rất chỉnh sửa bài.
@andrey trả lời trong một dòng (được thử nghiệm trong Swift 4.1 ):
extension UIResponder {
public var parentViewController: UIViewController? {
return next as? UIViewController ?? next?.parentViewController
}
}
sử dụng:
let vc: UIViewController = view.parentViewController
parentViewController
không thể được xác định public
nếu tiện ích mở rộng nằm trong cùng một tệp với UIView
bạn có thể đặt nó fileprivate
, nó sẽ biên dịch nhưng nó không hoạt động! 😐
Chỉ dành cho mục đích gỡ lỗi, bạn có thể gọi _viewDelegate
vào các khung nhìn để có các bộ điều khiển xem của chúng. Đây là API riêng tư, vì vậy không an toàn cho App Store, nhưng để gỡ lỗi thì nó rất hữu ích.
Các phương pháp hữu ích khác:
_viewControllerForAncestor
- có được bộ điều khiển đầu tiên quản lý chế độ xem trong chuỗi giám sát. (cảm ơn n00neimp0rtant)_rootAncestorViewController
- nhận bộ điều khiển tổ tiên có phân cấp chế độ xem được đặt trong cửa sổ hiện tại._viewControllerForAncestor
sẽ duyệt qua các giám sát cho đến khi tìm thấy cái đầu tiên thuộc về bộ điều khiển khung nhìn.
Để có được tham chiếu đến UIViewContoder có UIView, bạn có thể tạo phần mở rộng của UIResponder (siêu hạng cho UIView và UIViewContoder), cho phép đi qua chuỗi phản hồi và do đó tiếp cận với UIViewContoder (nếu không trả về nil).
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
Cách nhanh chóng và chung chung trong 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)
}
Nếu bạn không quen thuộc với mã và bạn muốn tìm lõi của ViewContoder cho chế độ xem đã cho, thì bạn có thể thử:
po (UIView *) 0x7fe523bd3000 po [(UIView *) 0x7fe523bd3000 nextResponder] po [[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] po [[[(UIView *) 0x7fe523bd3000 nextResponder] nextResponder] nextResponder] ...
Trong hầu hết các trường hợp, bạn sẽ nhận được UIView, nhưng đôi khi sẽ có lớp dựa trên UIViewControll.
Tôi nghĩ rằng bạn có thể truyền vòi cho bộ điều khiển xem và để nó xử lý nó. Đây là cách tiếp cận dễ chấp nhận hơn. Đối với việc truy cập bộ điều khiển chế độ xem từ chế độ xem của nó, bạn nên duy trì tham chiếu đến bộ điều khiển chế độ xem, vì không có cách nào khác. Xem chủ đề này, nó có thể giúp: Truy cập bộ điều khiển xem từ chế độ xem
Thêm loại mã an toàn cho 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
}
}
Than ôi, điều này là không thể trừ khi bạn phân lớp khung nhìn và cung cấp cho nó một thuộc tính cá thể hoặc tương tự để lưu trữ tham chiếu của trình điều khiển khung nhìn bên trong nó khi chế độ xem được thêm vào cảnh ...
Trong hầu hết các trường hợp - rất dễ để giải quyết vấn đề ban đầu của bài đăng này vì hầu hết các bộ điều khiển xem là các thực thể nổi tiếng với lập trình viên chịu trách nhiệm thêm bất kỳ cuộc phỏng vấn nào vào Chế độ xem của ViewContoder ;-) Đó là lý do tại sao tôi đoán rằng Apple không bao giờ bận tâm để thêm tài sản đó.
Hơi muộn một chút, nhưng đây là một tiện ích mở rộng cho phép bạn tìm phản hồi của bất kỳ loại nào, bao gồm cả ViewContoder.
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
}
}
Nếu bạn đặt điểm dừng, bạn có thể dán điểm này vào trình gỡ lỗi để in phân cấp chế độ xem:
po [[UIWindow keyWindow] recursiveDescription]
Bạn sẽ có thể tìm thấy cha mẹ của bạn ở đâu đó trong mớ hỗn độn đó :)
recursiveDescription
chỉ in phân cấp khung nhìn , không in bộ điều khiển xem.