मैं UINavigationController से एक दृश्य को कैसे पॉप कर सकता हूं और इसे एक ऑपरेशन में दूसरे के साथ बदल सकता हूं?


84

मेरे पास एक एप्लिकेशन है जहां मुझे एक UINavigationController के ढेर से एक दृश्य को हटाने और इसे दूसरे के साथ बदलने की आवश्यकता है। स्थिति यह है कि पहला दृश्य एक संपादन योग्य आइटम बनाता है और फिर आइटम के लिए एक संपादक के साथ खुद को बदलता है। जब मैं पहले दृश्य के भीतर स्पष्ट समाधान करता हूं:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[self retain];
[self.navigationController popViewControllerAnimated: NO];
[self.navigationController pushViewController: mevc animated: YES];
[self release];

मुझे बहुत अजीब व्यवहार मिलता है। आमतौर पर संपादक दृश्य दिखाई देगा, लेकिन अगर मैं नेवी बार पर बैक बटन का उपयोग करने की कोशिश करता हूं तो मुझे अतिरिक्त स्क्रीन, कुछ खाली, और कुछ बस खराब हो जाते हैं। शीर्षक भी यादृच्छिक हो जाता है। यह ऐसा है जैसे नौसेना स्टैक पूरी तरह से hosed है।

इस समस्या के लिए एक बेहतर तरीका क्या होगा?

धन्यवाद, मैट

जवाबों:


137

मुझे पता चला है कि आपको मैन्युअल रूप से viewControllersसंपत्ति के साथ खिलवाड़ करने की आवश्यकता नहीं है । मूल रूप से इस बारे में 2 मुश्किल बातें हैं।

  1. self.navigationControllerनेविगेशन नियंत्रक के ढेर पर वर्तमान में नहीं है, nilतो वापस आ जाएगा self। इससे पहले कि आप इसे एक्सेस करने से पहले इसे स्थानीय वेरिएबल में सेव कर लें।
  2. आपके पास retain(और ठीक से release) selfया वह वस्तु जो आपके पास मौजूद विधि का स्वामी है, को विचलित किया जाएगा, जिससे विचित्रता पैदा होगी।

एक बार जब आप उस प्रस्तुतिकरण को करते हैं, तो बस पॉप करते हैं और सामान्य रूप से धक्का देते हैं। यह कोड तुरन्त अन्य के साथ शीर्ष नियंत्रक को बदल देगा।

// locally store the navigation controller since
// self.navigationController will be nil once we are popped
UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

// Pop this controller and replace with another
[navController popViewControllerAnimated:NO];
[navController pushViewController:someViewController animated:NO];

उस अंतिम पंक्ति में यदि आप में परिवर्तन करते animatedहैं YES, तो नई स्क्रीन वास्तव में चेतन होगी और आपके द्वारा अभी-अभी पॉप किए गए नियंत्रक को चेतन किया जाएगा। बहुत अच्छा लग रहा है!


प्रतिभाशाली! ज्यादा बेहतर समाधान
एमएमबी

बहुत बढ़िया। हालाँकि मुझे [[self selfelor autorelease] कहने की जरूरत नहीं थी, फिर भी यह ठीक काम करता है।
59मज

4
शायद एक स्पष्ट अलावा, लेकिन आप संक्रमण को चेतन करने के लिए एक एनीमेशन ब्लॉक में ऊपर कोड डाल सकते हैं: [UIView startAnimations: @ "Flip देखें" संदर्भ: nil]; [उविवे सेटअनीमेशनडेशन: ०. ;०]; [UIView setAnimationCurve: UIViewAnimationCurveEaseInOut]; [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView: navController.view कैश: NO]; [navController pushViewController: newController animated: YES]; [UIView प्रतिबद्धताओं];
मार्टिन

11
एआरसी के साथ महान काम करता है बस बनाए रखने / autorelease लाइन को हटाकर।
इयान टेरेल

2
@TomerPeled हाँ, यह उत्तर लगभग 5 साल पुराना है ... मुझे लगता है कि iOS 3 में ऐसा ही था। एपीआई काफी बदल गए हैं, मुझे यकीन नहीं है कि यह अब तक का सबसे अच्छा जवाब है।
एलेक्स वेन

56

निम्नलिखित दृष्टिकोण मुझे अच्छा लगता है, और एआरसी के साथ भी अच्छा काम करता है:

UIViewController *newVC = [[UIViewController alloc] init];
// Replace the current view controller
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
[viewControllers removeLastObject];
[viewControllers addObject:newVC];
[[self navigationController] setViewControllers:viewControllers animated:YES];

1
@LukeRogers, यह मेरे लिए निम्न चेतावनी का कारण बनता है: एक अप्रत्याशित स्थिति में एक नेविगेशन संक्रमण को समाप्त करना। नेविगेशन बार सबव्यू ट्री दूषित हो सकता है। इसे दबाने का कोई तरीका?
zaitsman

इस समाधान का उपयोग करते हुए, आप पॉपओवर को अधिलेखित कर देते हैं। और डिटेल व्यू में दिखाने के लिए, आपके कोड को पढ़ना चाहिए:if(indexPath.row == 0){UIViewController *newVC = [[UIViewController alloc] init];newVC = [self.storyboard instantiateViewControllerWithIdentifier:@"Item1VC"]; NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[_detailViewController.navigationController viewControllers]]; [viewControllers removeLastObject];[viewControllers addObject:newVC]; [_detailViewController.navigationController setViewControllers:viewControllers animated:YES];}
LAOMUSIC ARTS

जिसे मैं ढूंढ रहा था।
JERC

9

अनुभव से, आपको viewControllersसीधे UINavigationController की संपत्ति के साथ फेल होना है । कुछ इस तरह काम करना चाहिए:

MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

[[self retain] autorelease];
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
self.navigationController.viewControllers = controllers;
[self.navigationController pushViewController:mevc animated: YES];

ध्यान दें: मैंने रिट / रिलीज को बदलकर एक रिटेन / ऑटोरेलिज को बदल दिया है क्योंकि यह आमतौर पर अधिक मजबूत है - यदि आप बनाए रखने / रिलीज के बीच एक अपवाद होता है तो आप स्वयं को लीक कर लेंगे, लेकिन ऑटोरेलिज इसका ख्याल रखता है।


7

बहुत प्रयास (और केविन से कोड को ट्विक करने के बाद), मुझे आखिरकार पता चला कि स्टैक से पॉपअप किए जा रहे व्यू कंट्रोलर में यह कैसे करना है। समस्या यह है कि मैं कर रहा था कि self.navigationController नियंत्रक सरणी से अंतिम वस्तु को हटाने के बाद शून्य वापस आ रहा था। मुझे लगता है कि यह इस विधि लाइन नेविगेशन में UIViewController के लिए प्रलेखन में इस लाइन के कारण था "केवल एक नेविगेशन नियंत्रक लौटाता है यदि दृश्य नियंत्रक अपने स्टैक में है।"

मुझे लगता है कि एक बार जब वर्तमान दृश्य नियंत्रक को स्टैक से हटा दिया जाता है, तो इसकी नेविगेशनकंट्रोलर विधि शून्य वापस आ जाएगी।

यहाँ समायोजित कोड काम करता है:

UINavigationController *navController = self.navigationController;
MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];
[controllers removeLastObject];
navController.viewControllers = controllers;
[navController pushViewController:mevc animated: YES];

यह मुझे एक काला पूरा देता है!
लूमुसिक कला

4

धन्यवाद, यह वही था जो मुझे चाहिए था। मैंने इसे पृष्ठ कर्ल प्राप्त करने के लिए एक एनीमेशन में भी डाला:

        MyEditViewController *mevc = [[MYEditViewController alloc] initWithGizmo: gizmo];

    UINavigationController *navController = self.navigationController;      
    [[self retain] autorelease];

    [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration: 0.7];
    [UIView setAnimationTransition:<#UIViewAnimationTransitionCurlDown#> forView:navController.view cache:NO];

    [navController popViewControllerAnimated:NO];
    [navController pushViewController:mevc animated:NO];

    [UIView commitAnimations];

0.6 अवधि तेज है, 3GS और नए के लिए अच्छा है, 0.8 अभी भी 3 जी के लिए बहुत तेज है।

जोहान


आपका कोड वही है जो मैंने उपयोग किया है, बढ़िया! धन्यवाद। एक नोट: पृष्ठ कर्ल संक्रमण के साथ मुझे देखने के तल पर एक सफेद आर्टिफ़िस मिला (जो जानता है कि क्यों) लेकिन फ्लिप के साथ इसने ठीक काम किया। वैसे भी, यह अच्छा और कॉम्पैक्ट कोड है!
डेविड एच

3

यदि आप popToRootViewController द्वारा कोई अन्य दृश्य नियंत्रक दिखाना चाहते हैं, तो आपको निम्न कार्य करने की आवश्यकता है:

         UIViewController *newVC = [[WelcomeScreenVC alloc] initWithNibName:@"WelcomeScreenVC" bundle:[NSBundle mainBundle]];
            NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:[[self navigationController] viewControllers]];
            [viewControllers removeAllObjects];
            [viewControllers addObject:newVC];
            [[self navigationController] setViewControllers:viewControllers animated:NO];

अब, आपके सभी पिछले स्टैक को हटा दिया जाएगा और आपके आवश्यक rootViewController के साथ नया स्टैक बनाया जाएगा।


1

मुझे हाल ही में इसी तरह का काम करना था और माइकल्स जवाब पर अपना समाधान आधारित था। मेरे मामले में मुझे नेविगेशन स्टैक से दो व्यू कंट्रोलर हटाने थे और फिर एक नया व्यू कंट्रोलर जोड़ना था। कॉलिंग

[नियंत्रकों removeLastObject];
दो बार, मेरे मामले में ठीक काम किया।

UINavigationController *navController = self.navigationController;

// retain ourselves so that the controller will still exist once it's popped off
[[self retain] autorelease];

searchViewController = [[SearchViewController alloc] init];    
NSMutableArray *controllers = [[self.navigationController.viewControllers mutableCopy] autorelease];

[controllers removeLastObject];
// In my case I want to go up two, then push one..
[controllers removeLastObject];
navController.viewControllers = controllers;

NSLog(@"controllers: %@",controllers);
controllers = nil;

[navController pushViewController:searchViewController animated: NO];


1

यह UINavigationControllerउदाहरण विधि काम कर सकती है ...

जब तक निर्दिष्ट दृश्य नियंत्रक शीर्ष दृश्य नियंत्रक नहीं होता तब तक नियंत्रक देखता है और फिर प्रदर्शन को अपडेट करता है।

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated

1

यहां एक और दृष्टिकोण है जिसे व्यू कॉन्ट्रोलर्स सरणी के साथ सीधे गड़बड़ करने की आवश्यकता नहीं है। जांचें कि क्या नियंत्रक अभी तक पॉप-अप किया गया है, यदि ऐसा है तो इसे धक्का दें।

TasksViewController *taskViewController = [[TasksViewController alloc] initWithNibName:nil bundle:nil];

if ([navigationController.viewControllers indexOfObject:taskViewController] == NSNotFound)
{
    [navigationController pushViewController:taskViewController animated:animated];
}
else
{
    [navigationController popToViewController:taskViewController animated:animated];
}

1
NSMutableArray *controllers = [self.navigationController.viewControllers mutableCopy];
    for(int i=0;i<controllers.count;i++){
       [controllers removeLastObject];
    }
 self.navigationController.viewControllers = controllers;

यह कंसोल में मेरे लिए एक चेतावनी का कारण बनता है - एक अप्रत्याशित स्थिति में एक नेविगेशन संक्रमण को खत्म करना। नेविगेशन बार सबव्यू ट्री दूषित हो सकता है। इसे दबाने का कोई तरीका?
zaitsman

1

यह करने के लिए मेरा पसंदीदा तरीका UINavigationController पर एक श्रेणी के साथ है। निम्नलिखित काम करना चाहिए:

UINavigationController + Helpers.h #import

@interface UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller;

@end

UINavigationController + हेल्पर्स।
#import "UINavigationController + Helpers.h"

@implementation UINavigationController (Helpers)

- (UIViewController*) replaceTopViewControllerWithViewController: (UIViewController*) controller {
    UIViewController* topController = self.viewControllers.lastObject;
    [[topController retain] autorelease];
    UIViewController* poppedViewController = [self popViewControllerAnimated:NO];
    [self pushViewController:controller animated:NO];
    return poppedViewController;
}

@end

फिर अपने व्यू कंट्रोलर से, आप टॉप व्यू को इस तरह से एक नए से बदल सकते हैं:

[self.navigationController replaceTopViewControllerWithViewController: newController];

0

आप नेविगेशन व्यू कंट्रोलर्स एरे से देख सकते हैं, जो आपको नेविगेशन स्टैक में जोड़े गए सभी व्यू कंट्रोलर देता है। उस सरणी का उपयोग करके आप विशिष्ट दृश्य नियंत्रक पर वापस नेविगेट कर सकते हैं।


0

मोनोटॉक / xamarin IOS के लिए:

UISplitViewController वर्ग के अंदर;

UINavigationController mainNav = this._navController; 
//List<UIViewController> controllers = mainNav.ViewControllers.ToList();
mainNav.ViewControllers = new UIViewController[] { }; 
mainNav.PushViewController(detail, true);//to have the animation

0

वैकल्पिक रूप से,

आप के बाद होने categoryसे बचने के self.navigationControllerलिए उपयोग कर सकते हैंnilpopViewControllerAnimated

बस पॉप और पुश, यह समझना आसान है, उपयोग करने की आवश्यकता नहीं है viewControllers...।

// UINavigationController+Helper.h
@interface UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated;

@end


// UINavigationController+Helper.m
@implementation UINavigationController (Helper)

- (UIViewController*) popThenPushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    UIViewController *v =[self popViewControllerAnimated:NO];

    [self pushViewController:viewController animated:animated];

    return v;
}
@end

आपके ViewController में

// #import "UINavigationController+Helper.h"
// invoke in your code
UIViewController *v= [[MyNewViewController alloc] init];

[self.navigationController popThenPushViewController:v animated:YES];

RELEASE_SAFELY(v);

0

उत्तर बिल्कुल नहीं, लेकिन कुछ परिदृश्यों में मदद का हो सकता है (उदाहरण के लिए मेरा):

अगर आपको व्यू कॉन्ट्रोलर सी को पॉप करने की जरूरत है और ए (वन बेलो सी) के बजाय बी (स्टैक से बाहर) जाएं, तो बी को सी से पहले पुश करना संभव है, और स्टैक पर सभी 3 हैं। B को अदृश्य रखते हुए, और केवल C या C और B को पॉप करने के लिए चयन करके, आप उसी प्रभाव को प्राप्त कर सकते हैं।

प्रारंभिक समस्या ए -> सी (मैं सी को पॉप और बी को स्टैक से बाहर दिखाना चाहता हूं)

संभव समाधान A -> B (धकेल दिया गया अदृश्य) -> C (जब मैं पॉप करता हूँ, तो मैं B दिखाना चाहता हूँ या इसे पॉप भी करता हूँ)


0

मैं एनीमेशन रखने के लिए इस समाधान का उपयोग करता हूं।

[self.navigationController pushViewController:controller animated:YES];
NSMutableArray *newControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[newControllers removeObject:newControllers[newControllers.count - 2]];
[self.navigationController setViewControllers:newControllers];
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.