वहाँ है एक अंतर्निहित तरीका एक से प्राप्त करने के लिए UIView
अपने को UIViewController
? मुझे पता है कि आप UIViewController
इसके UIView
माध्यम से प्राप्त कर सकते हैं [self view]
लेकिन मैं सोच रहा था कि क्या कोई उल्टा संदर्भ है?
वहाँ है एक अंतर्निहित तरीका एक से प्राप्त करने के लिए UIView
अपने को UIViewController
? मुझे पता है कि आप UIViewController
इसके UIView
माध्यम से प्राप्त कर सकते हैं [self view]
लेकिन मैं सोच रहा था कि क्या कोई उल्टा संदर्भ है?
जवाबों:
चूंकि यह लंबे समय से स्वीकृत उत्तर है, मुझे लगता है कि मुझे बेहतर उत्तर के साथ इसे सुधारने की आवश्यकता है।
जरूरत पर कुछ टिप्पणियाँ:
इसे कैसे लागू किया जाए, इसका एक उदाहरण:
@protocol MyViewDelegate < NSObject >
- (void)viewActionHappened;
@end
@interface MyView : UIView
@property (nonatomic, assign) MyViewDelegate delegate;
@end
@interface MyViewController < MyViewDelegate >
@end
दृश्य अपने प्रतिनिधि ( UITableView
उदाहरण के लिए, उदाहरण के लिए) के साथ इंटरफेस करता है और यह परवाह नहीं करता है कि क्या यह दृश्य नियंत्रक या किसी अन्य वर्ग में लागू होता है जिसे आप उपयोग कर रहे हैं।
मेरा मूल उत्तर इस प्रकार है: मैं इसकी अनुशंसा नहीं करता हूं, न ही बाकी उत्तर जहां व्यू कंट्रोलर की सीधी पहुंच है
इसे करने का कोई अंतर्निहित तरीका नहीं है। जबकि आप इंटरफ़ेस बिल्डर में इन IBOutlet
पर UIView
और जोड़कर इसके चारों ओर प्राप्त कर सकते हैं , यह अनुशंसित नहीं है। व्यू कंट्रोलर के बारे में जानकारी नहीं होनी चाहिए। इसके बजाय, आपको @Phil M सुझाव के रूप में करना चाहिए और प्रतिनिधि के रूप में उपयोग किए जाने के लिए एक प्रोटोकॉल बनाना चाहिए।
ब्रॉक द्वारा पोस्ट किए गए उदाहरण का उपयोग करते हुए, मैंने इसे संशोधित किया ताकि यह UIViewController के बजाय UIView की एक श्रेणी हो और इसे पुनरावर्ती बना दिया ताकि कोई भी सबव्यू (उम्मीद) मूल UIViewController को ढूंढ सके।
@interface UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController;
- (id) traverseResponderChainForUIViewController;
@end
@implementation UIView (FindUIViewController)
- (UIViewController *) firstAvailableUIViewController {
// convenience function for casting and to "mask" the recursive function
return (UIViewController *)[self traverseResponderChainForUIViewController];
}
- (id) traverseResponderChainForUIViewController {
id nextResponder = [self nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]]) {
return nextResponder;
} else if ([nextResponder isKindOfClass:[UIView class]]) {
return [nextResponder traverseResponderChainForUIViewController];
} else {
return nil;
}
}
@end
इस कोड का उपयोग करने के लिए, इसे एक नई कक्षा फ़ाइल (मुझे "UIKitCategories" नाम दिया गया है) में जोड़ें और वर्ग डेटा हटा दें ... शीर्ष लेख में @interface, और .m फ़ाइल में @ कार्यान्वयन लागू करें। फिर अपने प्रोजेक्ट में, #import "UIKitCategories.h" और UIView कोड का उपयोग करें:
// from a UIView subclass... returns nil if UIViewController not available
UIViewController * myController = [self firstAvailableUIViewController];
UIView
का एक उपवर्ग है UIResponder
। एक कार्यान्वयन के साथ UIResponder
विधि देता है -nextResponder
जो वापस लौटता है nil
। UIView
इस पद्धति को ओवरराइड करता है, जैसा कि UIResponder
(किसी कारण से UIView
) के रूप में प्रलेखित किया गया है: यदि दृश्य में दृश्य नियंत्रक है, तो यह वापस आ जाता है -nextResponder
। यदि कोई दृश्य नियंत्रक नहीं है, तो विधि पर्यवेक्षण को वापस कर देगी।
इसे अपने प्रोजेक्ट में जोड़ें और आप रोल करने के लिए तैयार हैं।
@interface UIView (APIFix)
- (UIViewController *)viewController;
@end
@implementation UIView (APIFix)
- (UIViewController *)viewController {
if ([self.nextResponder isKindOfClass:UIViewController.class])
return (UIViewController *)self.nextResponder;
else
return nil;
}
@end
अब UIView
दृश्य नियंत्रक को वापस करने के लिए एक कार्य विधि है।
UIView
और के बीच प्रत्युत्तर श्रृंखला में कुछ भी न हो UIViewController
। पुनरावृत्ति की विशेषता फिल एम का जवाब जाने का रास्ता है।
मैं UIView पर एक श्रेणी जोड़ने के लिए बिना पूर्ण उत्तरदाता श्रृंखला को ट्रेस करने के लिए अधिक हल्के दृष्टिकोण का सुझाव दूंगा:
@implementation MyUIViewSubclass
- (UIViewController *)viewController {
UIResponder *responder = self;
while (![responder isKindOfClass:[UIViewController class]]) {
responder = [responder nextResponder];
if (nil == responder) {
break;
}
}
return (UIViewController *)responder;
}
@end
पहले से दिए गए कई उत्तरों को जोड़ते हुए, मैं इसे अपने कार्यान्वयन के साथ शिपिंग कर रहा हूं:
@implementation UIView (AppNameAdditions)
- (UIViewController *)appName_viewController {
/// Finds the view's view controller.
// Take the view controller class object here and avoid sending the same message iteratively unnecessarily.
Class vcc = [UIViewController class];
// Traverse responder chain. Return first found view controller, which will be the view's view controller.
UIResponder *responder = self;
while ((responder = [responder nextResponder]))
if ([responder isKindOfClass: vcc])
return (UIViewController *)responder;
// If the view controller isn't found, return nil.
return nil;
}
@end
श्रेणी मेरे एआरसी-सक्षम स्थिर पुस्तकालय का हिस्सा है जिसे मैं अपने द्वारा बनाए गए प्रत्येक एप्लिकेशन पर शिप करता हूं। यह कई बार परीक्षण किया गया है और मुझे कोई समस्या या लीक नहीं मिली।
पुनश्च: आपको किसी श्रेणी का उपयोग करने की आवश्यकता नहीं है जैसे मैंने किया था यदि संबंधित दृश्य आपका उपवर्ग है। बाद के मामले में, बस अपने उपवर्ग में विधि रखो और तुम जाने के लिए अच्छे हो।
भले ही यह तकनीकी रूप से pgb अनुशंसा के रूप में हल किया जा सकता है , IMHO, यह एक डिजाइन दोष है। व्यूअर को कंट्रोलर के बारे में पता होना चाहिए।
मैंने डी उत्तर को संशोधित किया है ताकि मैं इसे देखने के लिए कोई भी दृश्य, बटन, लेबल आदि पास कर सकूं UIViewController
। यहाँ मेरा कोड है।
+(UIViewController *)viewController:(id)view {
UIResponder *responder = view;
while (![responder isKindOfClass:[UIViewController class]]) {
responder = [responder nextResponder];
if (nil == responder) {
break;
}
}
return (UIViewController *)responder;
}
स्विफ्ट 3 संस्करण संपादित करें
class func viewController(_ view: UIView) -> UIViewController {
var responder: UIResponder? = view
while !(responder is UIViewController) {
responder = responder?.next
if nil == responder {
break
}
}
return (responder as? UIViewController)!
}
2 संपादित करें: - स्विफ्ट एक्सटेंशन
extension UIView
{
//Get Parent View Controller from any view
func parentViewController() -> UIViewController {
var responder: UIResponder? = self
while !(responder is UIViewController) {
responder = responder?.next
if nil == responder {
break
}
}
return (responder as? UIViewController)!
}
}
मत भूलो कि आप विंडो के लिए रूट व्यू कंट्रोलर तक पहुंच प्राप्त कर सकते हैं जो कि दृश्य एक सबव्यू है। वहाँ से, यदि आप उदाहरण के लिए एक नेविगेशन व्यू कंट्रोलर का उपयोग कर रहे हैं और उस पर एक नया दृश्य पुश करना चाहते हैं:
[[[[self window] rootViewController] navigationController] pushViewController:newController animated:YES];
हालाँकि आपको पहले ठीक से विंडो के rootViewController प्रॉपर्टी को सेट करना होगा। ऐसा तब करें जब आप पहली बार अपने ऐप के प्रतिनिधि में नियंत्रक बनाते हैं:
-(void) applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController *controller = [[YourRootViewController] alloc] init];
[window setRootViewController: controller];
navigationController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
[controller release];
[window addSubview:[[self navigationController] view]];
[window makeKeyAndVisible];
}
[[self navigationController] view]
विंडो का "मुख्य" (सब) व्यू है, विंडो की rootViewController
प्रॉपर्टी को सेट करना पड़ता है, navigationController
जो "मेन" व्यू को तुरंत कंट्रोल करता है।
हालांकि ये उत्तर तकनीकी रूप से सही हैं, जिनमें उशॉक्स भी शामिल है, मुझे लगता है कि स्वीकृत तरीका एक नया प्रोटोकॉल लागू करना या किसी मौजूदा का फिर से उपयोग करना है। एक प्रोटोकॉल पर्यवेक्षक को उनके बीच एक मेल स्लॉट डालने की तरह, प्रेक्षित से निरीक्षण करता है। वास्तव में, यह वही है जो गेब्रियल पुश व्यूकंट्रोलर विधि के आह्वान के माध्यम से करता है; दृश्य "जानता है" यह उचित प्रोटोकॉल है कि विनम्रतापूर्वक अपने नेविगेशनकोंट्रोलर से एक दृश्य पुश करने के लिए कहें, क्योंकि व्यूकंट्रोलर नेविगेशनकंट्रोलर प्रोटोकॉल के अनुरूप है। जब आप अपना स्वयं का प्रोटोकॉल बना सकते हैं, तो केवल गेब्रियल के उदाहरण का उपयोग करके और यूनावीगेशन कॉन्ट्रोलर प्रोटोकॉल का फिर से उपयोग करना ठीक है।
मैं एक ऐसी स्थिति पर ठोकर खाई, जहां मेरे पास एक छोटा घटक है जिसका मैं पुन: उपयोग करना चाहता हूं, और पुन: प्रयोज्य दृश्य में कुछ कोड जोड़ा (यह वास्तव में एक बटन से अधिक नहीं है जो खुलता है PopoverController
)।
जबकि यह iPad में ठीक काम करता है ( UIPopoverController
खुद को प्रस्तुत करता है, इसके लिए कोई संदर्भ की आवश्यकता नहीं है UIViewController
), काम करने के लिए समान कोड प्राप्त करने का मतलब है अचानक आपके अपने presentViewController
से संदर्भित करना UIViewController
। Kinda असंगत अधिकार?
जैसा कि पहले उल्लेख किया गया है, यह आपके यूविवि में तर्क रखने का सबसे अच्छा तरीका नहीं है। लेकिन कोड की कुछ पंक्तियों को एक अलग नियंत्रक में लपेटना वास्तव में बेकार लगा।
किसी भी तरह से, यहाँ एक त्वरित समाधान है, जो किसी भी यूवाईवाईवाई में एक नई संपत्ति जोड़ता है:
extension UIView {
var viewController: UIViewController? {
var responder: UIResponder? = self
while responder != nil {
if let responder = responder as? UIViewController {
return responder
}
responder = responder?.nextResponder()
}
return nil
}
}
मुझे नहीं लगता कि यह पता लगाने के लिए "बुरा" विचार है कि कुछ मामलों के लिए कौन नियंत्रक है। इस नियंत्रक के संदर्भ को बचाने के लिए एक बुरा विचार क्या हो सकता है क्योंकि यह पर्यवेक्षकों के परिवर्तन के रूप में बदल सकता है। मेरे मामले में मेरे पास एक गेटर है जो उत्तरदाता श्रृंखला का पता लगाता है।
//.h
@property (nonatomic, readonly) UIViewController * viewController;
//।म
- (UIViewController *)viewController
{
for (UIResponder * nextResponder = self.nextResponder;
nextResponder;
nextResponder = nextResponder.nextResponder)
{
if ([nextResponder isKindOfClass:[UIViewController class]])
return (UIViewController *)nextResponder;
}
// Not found
NSLog(@"%@ doesn't seem to have a viewController". self);
return nil;
}
व्यूकंट्रोलर खोजने के लिए लूप करते समय सबसे सरल।
-(UIViewController*)viewController
{
UIResponder *nextResponder = self;
do
{
nextResponder = [nextResponder nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
return (UIViewController*)nextResponder;
} while (nextResponder != nil);
return nil;
}
(अन्य उत्तरों की तुलना में अधिक संक्षिप्त)
fileprivate extension UIView {
var firstViewController: UIViewController? {
let firstViewController = sequence(first: self, next: { $0.next }).first(where: { $0 is UIViewController })
return firstViewController as? UIViewController
}
}
मेरे उपयोग के मामले जिसके लिए मैं पहली बार देखने का उपयोग करने की जरूरत है UIViewController
: मुझे लगता है कि wraps चारों ओर एक वस्तु है AVPlayer
/ AVPlayerViewController
और मैं एक सरल प्रदान करना चाहते हैं show(in view: UIView)
विधि है कि एम्बेड कर देंगे AVPlayerViewController
में view
। कि के लिए, मैं का उपयोग करने की जरूरत है view
's UIViewController
।
यह सीधे सवाल का जवाब नहीं देता है, बल्कि सवाल के इरादे के बारे में एक धारणा बनाता है।
यदि आपके पास एक दृश्य है और उस दृश्य में आपको किसी अन्य ऑब्जेक्ट पर एक विधि को कॉल करने की आवश्यकता है, जैसे दृश्य नियंत्रक कहते हैं, तो आप इसके बजाय NSNotificationCenter का उपयोग कर सकते हैं।
सबसे पहले अपने नोटिफिकेशन स्ट्रिंग को हेडर फाइल में बनाएं
#define SLCopyStringNotification @"ShaoloCopyStringNotification"
आपके विचार में कॉल करें PostNotificationName:
- (IBAction) copyString:(id)sender
{
[[NSNotificationCenter defaultCenter] postNotificationName:SLCopyStringNotification object:nil];
}
फिर अपने दृश्य नियंत्रक में आप एक पर्यवेक्षक जोड़ते हैं। मैं इसे देखने में करता हूँ
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(copyString:)
name:SLCopyStringNotification
object:nil];
}
अब (समान दृश्य नियंत्रक में भी) अपने तरीके को कार्यान्वित करें copyString: जैसा कि ऊपर दिए गए @selector में दर्शाया गया है।
- (IBAction) copyString:(id)sender
{
CalculatorResult* result = (CalculatorResult*)[[PercentCalculator sharedInstance].arrayTableDS objectAtIndex:([self.viewTableResults indexPathForSelectedRow].row)];
UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
[gpBoard setString:result.stringResult];
}
मैं यह नहीं कह रहा हूं कि ऐसा करने का यह सही तरीका है, यह सिर्फ पहली प्रतिक्रिया श्रृंखला को चलाने की तुलना में क्लीनर लगता है। मैंने इस कोड का उपयोग एक UIMableView पर एक UIMenuController को लागू करने और इवेंट को वापस UIViewController तक करने के लिए किया ताकि मैं डेटा के साथ कुछ कर सकूं।
यह निश्चित रूप से एक बुरा विचार और एक गलत डिजाइन है, लेकिन मुझे यकीन है कि हम सभी @P___ द्वारा प्रस्तावित सर्वश्रेष्ठ उत्तर के एक स्विफ्ट समाधान का आनंद ले सकते हैं:
static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
if let nextResponder = responder.nextResponder() {
if let nextResp = nextResponder as? UIViewController {
return nextResp
} else {
return traverseResponderChainForUIViewController(nextResponder)
}
}
return nil
}
return traverseResponderChainForUIViewController(responder)
}
यदि आपका इरादा साधारण चीजें करना है, जैसे कि एक मॉडल संवाद या ट्रैकिंग डेटा, जो किसी प्रोटोकॉल के उपयोग को सही नहीं ठहराता है। मैं व्यक्तिगत रूप से इस फ़ंक्शन को एक उपयोगिता ऑब्जेक्ट में संग्रहीत करता हूं, आप इसका उपयोग किसी भी चीज़ से कर सकते हैं जो कि UIResponder प्रोटोकॉल को लागू करता है:
if let viewController = MyUtilityClass.firstAvailableUIViewController(self) {}
सारा श्रेय @Phil_M को
शायद मुझे यहां आने में देर हो गई। लेकिन इस स्थिति में मुझे श्रेणी (प्रदूषण) पसंद नहीं है। मैं इस तरह से प्यार करता हूँ:
#define UIViewParentController(__view) ({ \
UIResponder *__responder = __view; \
while ([__responder isKindOfClass:[UIView class]]) \
__responder = [__responder nextResponder]; \
(UIViewController *)__responder; \
})
स्विफ्टियर समाधान
extension UIView {
var parentViewController: UIViewController? {
for responder in sequence(first: self, next: { $0.next }) {
if let viewController = responder as? UIViewController {
return viewController
}
}
return nil
}
}
sequence
बिट) का अच्छा प्रदर्शन है । इसलिए यदि "स्विफ्टी" का अर्थ "अधिक कार्यात्मक" है, तो मुझे लगता है कि यह अधिक स्विफ्टी है।
स्विफ्ट 4 के लिए अपडेट किया गया संस्करण: @Phil_M और @ paul-slm के लिए धन्यवाद
static func firstAvailableUIViewController(fromResponder responder: UIResponder) -> UIViewController? {
func traverseResponderChainForUIViewController(responder: UIResponder) -> UIViewController? {
if let nextResponder = responder.next {
if let nextResp = nextResponder as? UIViewController {
return nextResp
} else {
return traverseResponderChainForUIViewController(responder: nextResponder)
}
}
return nil
}
return traverseResponderChainForUIViewController(responder: responder)
}
स्विफ्ट 4 संस्करण
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
}
उदाहरण का उपयोग करें
if let parent = self.view.parentViewController{
}
return
अब कीवर्ड की कोई आवश्यकता नहीं है nowसमाधान 1:
extension UIView {
var parentViewController: UIViewController? {
sequence(first: self) { $0.next }
.first(where: { $0 is UIViewController })
.flatMap { $0 as? UIViewController }
}
}
समाधान 2:
extension UIView {
var parentViewController: UIViewController? {
sequence(first: self) { $0.next }
.compactMap{ $0 as? UIViewController }
.first
}
}
मेरे समाधान को संभवतः एक प्रकार का फर्जी माना जाएगा लेकिन मेरे पास भी ऐसी ही स्थिति थी जैसे मेयोनेज़ (मैं एक ईएजीएलव्यू में इशारे के जवाब में विचार स्विच करना चाहता था), और मुझे ईएजीएल का व्यू कंट्रोलर इस तरह मिला:
EAGLViewController *vc = ((EAGLAppDelegate*)[[UIApplication sharedApplication] delegate]).viewController;
EAGLViewController *vc = [(EAGLAppDelegate *)[UIApplication sharedApplication].delegate viewController];
।
ClassName *object
- तारांकन के साथ।
मुझे लगता है कि एक ऐसा मामला है जब प्रेक्षक को पर्यवेक्षक को सूचित करने की आवश्यकता है।
मैं एक ऐसी ही समस्या देखता हूं जहां UIViewController में UIView एक स्थिति का जवाब दे रहा है और उसे बैक बटन छिपाने के लिए पहले अपने पैरेंट व्यू कंट्रोलर को बताना होगा और फिर पूरा होने पर पैरेंट व्यू कंट्रोलर को बताना होगा कि उसे खुद को स्टैक से पॉप करने की जरूरत है।
मैं कोशिश कर रहा हूं कि प्रतिनिधियों के साथ कोई सफलता न हो।
मुझे समझ नहीं आता कि यह एक बुरा विचार क्यों होना चाहिए?
एक और आसान तरीका यह है कि आप अपना व्यू क्लास रखें और व्यू क्लास में व्यू कंट्रोलर की प्रॉपर्टी जोड़ें। आमतौर पर व्यू कंट्रोलर व्यू बनाता है और यहीं कंट्रोलर प्रॉपर्टी को सेट कर सकता है। मूल रूप से यह नियंत्रक के लिए चारों ओर (हैकिंग के साथ) खोज करने के बजाय है, नियंत्रक को अपने आप को दृश्य पर सेट करने के लिए - यह सरल है लेकिन समझ में आता है क्योंकि यह नियंत्रक है जो दृश्य को "नियंत्रित" करता है।
यदि आप इसे ऐप स्टोर पर अपलोड नहीं करने जा रहे हैं, तो आप UIView की एक निजी पद्धति का भी उपयोग कर सकते हैं।
@interface UIView(Private)
- (UIViewController *)_viewControllerForAncestor;
@end
// Later in the code
UIViewController *vc = [myView _viewControllerForAncestor];
var parentViewController: UIViewController? {
let s = sequence(first: self) { $0.next }
return s.compactMap { $0 as? UIViewController }.first
}
यदि आपका rootViewController UINavigationViewController है, जो कि AppDelegate वर्ग में स्थापित किया गया था, तो
+ (UIViewController *) getNearestViewController:(Class) c {
NSArray *arrVc = [[[[UIApplication sharedApplication] keyWindow] rootViewController] childViewControllers];
for (UIViewController *v in arrVc)
{
if ([v isKindOfClass:c])
{
return v;
}
}
return nil;}
जहां सी आवश्यक दृश्य नियंत्रकों वर्ग।
उपयोग:
RequiredViewController* rvc = [Utilities getNearestViewController:[RequiredViewController class]];
अब कोई रास्ता नहीं है।
मैं क्या करता हूं UIViewController पॉइंटर को UIView (या एक उपयुक्त उत्तराधिकार) पास करता है। मुझे खेद है कि मैं समस्या के लिए आईबी दृष्टिकोण के साथ मदद नहीं कर सकता क्योंकि मुझे आईबी पर विश्वास नहीं है।
पहले टिप्पणीकार का जवाब देने के लिए: कभी-कभी आपको यह जानने की जरूरत है कि आपको किसने बुलाया क्योंकि यह निर्धारित करता है कि आप क्या कर सकते हैं। उदाहरण के लिए एक डेटाबेस के साथ आप केवल पढ़ सकते हैं या पढ़ सकते हैं / लिख सकते हैं ...