UINavigationController प्रोग्रामेटिक रूप से UINavigationBar का एक कस्टम उपवर्ग सेट करें


92

क्या किसी को पता है कि मैं अपने कस्टम उपवर्ग का उपयोग कैसे कर सकता हूं UINavigationBarयदि मैं UINavigationControllerप्रोग्रामेटिक रूप से (आईबी के बिना) तुरंत काम करता हूं ?

UINavigationControllerIB में खींचें, मुझे नेविगेशन बार के तहत दिखाएगा और आइडेंटिटी इंस्पेक्टोरी का उपयोग करके मैं क्लास का प्रकार बदल सकता हूं और अपना उपवर्ग सेट UINavigationBarकर सकता हूं , लेकिन प्रोग्रामेटिक रूप से मैं नहीं कर सकता, navigationBarनेविगेशन कंट्रोलर की संपत्ति आसानी से है ...

मुझे नेविगेशन बार को प्रोग्रामेटिक रूप से कस्टमाइज़ करने के लिए क्या करना चाहिए? क्या आईबी "कोड" की तुलना में अधिक "शक्तिशाली" है? मेरा मानना ​​था कि आईबी में जो कुछ भी किया जा सकता है, वह प्रोग्रामेटिक रूप से भी किया जा सकता है।


क्या आपको कहीं और समाधान खोजने का सौभाग्य मिला है?
prendio2

क्या आपको इसके बारे में कोई जवाब मिलता है?
हृषिकेश बेताई

जवाबों:


89

आपको XIB के साथ KVC का उपयोग करने की आवश्यकता नहीं है।

[self.navigationController setValue:[[[CustomNavBar alloc]init] autorelease] forKeyPath:@"navigationBar"];

यह समाधान एक आकर्षण की तरह काम करता है (कम से कम आईओएस 5.1 पर)। अन्य सभी समाधान अधिक काम करने के तरीके की तरह लगते हैं। अभी भी नीचे की तलाश में है।
डैनियल

6
आपको नेविगेशन कंट्रोलर के लिए यह मुख्य पथ @ "नेविगेशनबेर" कैसा लगा। क्या आप इसे साझा कर सकते हैं
इकबाल खान

क्या आपको यकीन है कि इससे App अस्वीकृति नहीं होगी? मैंने इस दस्तावेज को वास्तव में कहीं भी नहीं देखा था।
बानी उप्पल

धन्यवाद! IOS 6 बीटा 3 में भी बढ़िया काम करता है! @ बानीअप्पल केवीसी निश्चित रूप से प्रलेखित है, हम इसे केवल एक कुंजी के साथ उपयोग कर रहे हैं जिसे खोजना थोड़ा मुश्किल है। मुझे लगता है कि यह मुख्य बिंदु है।
जोहान्स लुंड

9
ऐसा लगता है कि
हैके

66

IOS5 के बाद से, Apple सीधे ऐसा करने के लिए एक विधि प्रदान करता है। संदर्भ

UINavigationController *navigationController= [[UINavigationController alloc]initWithNavigationBarClass:[CustomNavBar class] toolbarClass:nil];
[navigationController setViewControllers:[NSArray arrayWithObject:yourRootViewController]];

@nonamelive वास्तव में, iOS6 में नहीं जोड़ा गया: developer.apple.com/library/ios/#releasenotes/General/…
पास्कलियस

7
हां, यह iOS 6 में जोड़ा गया है, लेकिन यह iOS 5 में भी समर्थित है। एक Apple इंजीनियर ने उल्लेख किया कि WWDC 2012 सत्र 216 में।
nonamelive

37

IOS 4 के रूप में, आप UINibइस समस्या को हल करने में मदद करने के लिए वर्ग का उपयोग कर सकते हैं ।

  1. अपना कस्टम UINavigationBarउपवर्ग बनाएँ ।
  2. एक खाली xib बनाएं, UINavigationControllerएकल ऑब्जेक्ट के रूप में जोड़ें ।
  3. के लिए वर्ग सेट UINavigationControllerके UINavigationBarअपने कस्टम उपवर्ग के लिए।
  4. इन विधियों में से किसी एक के माध्यम से अपना रूट व्यू कंट्रोलर सेट करें:
    • [navController setViewcontrollers[NSArray arrayWithObject:myRootVC]];
    • [navController pushViewController:myRootVC];

कोड में:

UINib *nib = [UINib nibWithNibName:@"YourCustomXib" bundle:nil];
UINavigationController *navController = 
             [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];


अब आपको UINavigationControllerअपने रिवाज के साथ मिल गया है UINavigationBar


इसके अलावा आप rootViewController कैसे सेट करते हैं?
मेमों

आप [navcontroller setViewControllers: [NSArray arrayWithObject: <your_ROOT_CONTROLLER>]] का उपयोग करें
coneybeare

क्षमा करें, आप उपयोग करते हैं [navcontroller pushViewController: <Your_ROOT_CONTROLLER> एनिमेटेड: NO]
coneybeare

3
IMHO यह कम से कम "हैकिश" तरीका है जिससे यह कभी-कभी अपरिहार्य हैक हो जाता है।
डेविड पिसानी

2
दरअसल, एक अजीब समस्या। जब मैं ऐसा करता हूं, तो नए नेविगेशनकंट्रोलर के नेविगेशन इटेम को नेविगेशनटैम पर सेट नहीं किया जा रहा है। यह देखने के लिए निर्दिष्ट संपत्ति है कि मैं (खाली) स्टैक पर जोर दे रहा हूं।
डेविड पिसानी

26

जहाँ तक मैं बता सकता हूँ, कभी-कभी UINavigationBar को subclass करना ज़रूरी होता है, कुछ अमानक रेस्टलिंग करने के लिए। कभी-कभी श्रेणियों का उपयोग करके ऐसा करने से बचना संभव है , लेकिन हमेशा नहीं।

वर्तमान में, जहां तक ​​मुझे पता है, एक UIViewController के भीतर एक कस्टम UINavigationBar सेट करने का एकमात्र तरीका IB (अर्थात, एक संग्रह के माध्यम से) है - यह शायद उस तरह से नहीं होना चाहिए, लेकिन अभी के लिए, हम इसके साथ रहते हैं।

यह अक्सर ठीक होता है, लेकिन कभी-कभी आईबी का उपयोग करना वास्तव में संभव नहीं होता है।

तो, मैंने तीन विकल्प देखे:

  1. उपवर्ग UINavigationBar और इसे IB में सभी को हुक करें, फिर प्रत्येक बार जब मैं एक UINavigationController चाहता था, तो निब को लोड करने के बारे में बताएं;
  2. उपवर्ग के बजाय, UINavigationBar के व्यवहार को बदलने के लिए एक श्रेणी के भीतर विधि प्रतिस्थापन का उपयोग करें , या
  3. उपवर्ग UINavigationBar और UINavigationController को संग्रहीत / अनारक्षित करने के बारे में थोड़ा विचार करना।

विकल्प 1 इस मामले में मेरे लिए अक्षम्य (या कम से कम बहुत कष्टप्रद) था, जैसा कि मुझे UINavigationController प्रोग्रामेटिक रूप से बनाने की आवश्यकता थी, 2 मेरी राय में थोड़ा खतरनाक और अंतिम उपाय का अधिक विकल्प है, इसलिए मैंने विकल्प 3 चुना।

मेरा दृष्टिकोण एक UINavigationController के 'टेम्पलेट' संग्रह बनाने के लिए था, और अराजक, जो इसे वापस कर रहा था initWithRootViewController

ऐसे:

IB में, मैंने UINavigationBont के लिए उपयुक्त वर्ग सेट के साथ एक UINavigationController बनाया।

फिर, मैंने मौजूदा नियंत्रक को लिया, और इसका उपयोग करके एक संग्रहीत प्रति सहेज ली +[NSKeyedArchiver archiveRootObject:toFile:]। मैंने सिर्फ ऐप प्रतिनिधि के भीतर, सिम्युलेटर में ऐसा किया है।

मैंने तब -i ध्वज के साथ 'xxd' उपयोगिता का उपयोग किया, सहेजी गई फ़ाइल से c कोड उत्पन्न करने के लिए, मेरे उपवर्ग ( xxd -i path/to/file) में संग्रहीत संस्करण को एम्बेड करने के लिए ।

भीतर initWithRootViewControllerमैं संग्रह से निकालने कि टेम्पलेट, और असंग्रहित के परिणाम के सेट स्व:

// This is the data from [NSKeyedArchiver archivedDataWithRootObject:controller], where
// controller is a CTNavigationController with navigation bar class set to CTNavigationBar,
// from IB.  This c code was created using 'xxd -i'
static unsigned char archived_controller[] = {
    0x62, 0x70, 0x6c, 0x69, 0x73, 0x74, 0x30, 0x30, 0xd4, 0x01, 0x02, 0x03,
    ...
};
static unsigned int archived_controller_len = 682;

...

- (id)initWithRootViewController:(UIViewController *)rootViewController {
     // Replace with unarchived view controller, necessary for the custom navigation bar
     [self release];
     self = (CTNavigationController*)[NSKeyedUnarchiver unarchiveObjectWithData:[NSData dataWithBytes:archived_controller length:archived_controller_len]];
     [self setViewControllers:[NSArray arrayWithObject:rootViewController]];
     return [self retain];
}

फिर, मैं सिर्फ अपने UIViewController उपवर्ग का एक नया उदाहरण ले सकता हूं जिसमें कस्टम नेविगेशन बार सेट है:

UIViewController *modalViewController = [[[CTNavigationController alloc] initWithRootViewController:myTableViewController] autorelease];
[self.navigationController presentModalViewController:modalViewController animated:YES];

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

मैं +layerClassUINavigationController के भीतर देखना चाहता हूं - +navigationBarClass- लेकिन अभी के लिए, यह काम करता है।


5

मैं "विकल्प 1" का उपयोग करता हूं

इसमें केवल UINavigationController के साथ एक nib-file बनाएं। और UINavigationBar Class को मेरे कस्टम क्लास में सेट करें।

self.navigationController = [[[NSBundle mainBundle] loadNibNamed:@"navigationbar" owner:self options:nil] lastObject];

[navigationController pushViewController:rootViewController animated:YES];

इसलिए निब-फाइल का उपयुक्त नाम लोड किया जाएगा। नाम: @ "नेवीगेशनकंट्रोलर? आप वास्तव में निब से संपूर्ण नेविगेशनकंट्रोलर प्राप्त कर रहे हैं और केवल बार ही नहीं। सही?
aeuryzm

यह मेरे लिए काम करता है, लेकिन जब मैं अपने टेबलव्यू में किसी भी विकल्प पर क्लिक करता हूं तो मैं बैक बटन खो देता हूं।
jfisk

5

माइकल का समाधान काम करता है, लेकिन आप NSKeyedArchiver और 'xxd' उपयोगिता से बच सकते हैं। बस उपवर्ग UINavigationController और ओवरराइड initWithRootViewController, सीधे आपके कस्टम नेविगेशनकंट्रोलर NIB को लोड करना:

- (id) initWithRootViewController:(UIViewController *)rootViewController
{
    [self release];
    self = [[[[NSBundle mainBundle] loadNibNamed:@"CTNavigationController" owner:nil options:nil] objectAtIndex:0] retain];  
    [self setViewControllers:[NSArray arrayWithObject:rootViewController]];
    return self;
}

4

अपडेट: का उपयोग object_SetClass()नहीं रह गया है जैसे कि iOS5 जीएम काम करता है। एक वैकल्पिक समाधान नीचे जोड़ा गया है।

NSKeyedUnarchiver का उपयोग मैन्युअल रूप से नौसेना बार के लिए अराजक वर्ग सेट करने के लिए करें।

   MyViewController *controller = [[[MyViewController alloc] init] autorelease];
   NSKeyedUnarchiver *unarchiver = [[[NSKeyedUnarchiver alloc] initForReadingWithData:[NSKeyedArchiver archivedDataWithRootObject:controller]] autorelease];
   [unarchiver setClass:[MyNavigationBar class] forClassName:@"UINavigationBar"];
   controller = [unarchiver decodeObjectForKey:@"root"];




नोट: यह मूल समाधान केवल पूर्व iOS5 काम करता है:

एक महान समाधान है, जो मैंने यहां पोस्ट किया है - सीधे अपने विचार में नवबर उपवर्ग को इंजेक्ट करें UINavigationController:

#import <objc/runtime.h>

- (void)viewDidLoad {
    [super viewDidLoad];

    object_setClass(self.navigationController.navigationBar, [MyNavBar class]);
    // the rest of your viewDidLoad code
}

जैसा कि मैंने पिछले उत्तर में संकेत दिया था कि आपने इसे पोस्ट किया है - iOS5 के गोल्ड मास्टर ने इसे तोड़ दिया। हालांकि अन्य विकल्प उपलब्ध हैं, इसलिए मैं एक वैकल्पिक विधि के साथ संपादन करूंगा।
याद रखें

1

एक परिदृश्य जो मैंने पाया है कि हमें श्रेणी के बजाय उपवर्ग का उपयोग करने की आवश्यकता है, पैटर्न छवि के साथ नेविगेशनबार बैकग्राउंडर सेट करना है, क्योंकि iOS5 में श्रेणी का उपयोग करते हुए ओवरराइटिंग ड्रा किसी भी अधिक काम नहीं करता है। यदि आप ios3.1-5.0 का समर्थन करना चाहते हैं, तो एकमात्र तरीका यह है कि आप नेविगेशनबार को उप-वर्ग कर सकते हैं।


1

ये श्रेणी विधियाँ खतरनाक हैं और नौसिखियों के लिए नहीं। इसके अलावा iOS4 और iOS5 अलग होने के साथ जटिलता, यह एक ऐसा क्षेत्र है जो कई लोगों के लिए कीड़े पैदा कर सकता है। यहाँ एक सरल उपवर्ग है जिसका उपयोग मैं iOS4.0 ~ iOS6.0 का समर्थन करता हूं और यह बहुत सरल है।

@interface XXXNavigatioNBar : UINavigationBar
@end

।म

#import "XXXNavigationBar.h"

#import <objc/runtime.h>

@implementation XXXNavigationBar

- (void) didMoveToSuperview {
    if( [self respondsToSelector: @selector(setBackgroundImage:forBarMetrics:)]) {
        //iOS5.0 and above has a system defined method -> use it
        [self setBackgroundImage: [UIImage imageNamed: @"nav-bar"]
                   forBarMetrics: UIBarMetricsDefault];
    }
    else {
        //iOS4.0 requires us to override drawRect:. BUT!!
        //If you override drawRect: on iOS5.0 the system default will break,
        //so we dynamically add this method if required
        IMP implementation = class_getMethodImplementation([self class], @selector(iOS4drawRect:));
        class_addMethod([self class], @selector(drawRect:), implementation, "v@:{name=CGRect}");
    }
}

- (void)iOS4drawRect: (CGRect) rect {
    UIImage* bg = [UIImage imageNamed:@"nav-bar-blue"];
    [bg drawInRect: rect];
}

@end

0

यह वर्ग को उपवर्ग करने के लिए अनुशंसित नहीं हैUINavigationBar । नेविगेशन बार को अनुकूलित करने का पसंदीदा तरीका यह है कि आप अपनी इच्छानुसार इसे प्रकट कर सकें और इच्छित व्यवहार प्राप्त करने के लिए एक प्रतिनिधि के साथ UIBarButtonItems के अंदर कस्टम दृश्यों का उपयोग करने के लिए यह गुण सेट कर सकें।

आप क्या करने की कोशिश कर रहे हैं जो उपवर्ग की आवश्यकता है?

इसके अलावा, मुझे नहीं लगता कि आईबी वास्तव में नेविगेशन बार की जगह ले रहा है। मुझे पूरा यकीन है कि यह केवल डिफॉल्ट को प्रदर्शित नहीं कर रहा है और आपके कस्टम नेव बार को एक सबव्यू के रूप में प्रदर्शित करता है। यदि आप UINavigationController.navigationBar कहते हैं, तो क्या आपको अपने बार का एक उदाहरण मिलता है?


2
हाय बेन, धन्यवाद। मुझे पता है कि UINavigationBar को उपखंड करने का बेहतर तरीका नहीं है, लेकिन मैं एक पृष्ठभूमि छवि को ड्रॉइंग ओवरराइड करना चाहूंगा: विधि। समस्या यह है कि IB का उपयोग करके मैं UINavigationController में नेविगेशन बार की कक्षा को बदल सकता हूं, लेकिन प्रोग्रामेटिक रूप से मैं नहीं कर सकता। और हाँ, आईबी वास्तव में नेविगेशन बार की जगह ले रहा है: NSLog (@ "% @", self.navigationController.navigationBar); <CustomNavigationBar: 0x1806160; baseClass = UINavigationBar; फ्रेम = (0 20; 320 44); ClipToBounds = YES; अपारदर्शी = ना; autoresize = डब्ल्यू; परत = <CALayer: 0x1806da0 >>
Duccio

मैं भी इसी तकनीक का उपयोग कर रहा हूं, और यह iOS5 (UINavigationBar श्रेणी की तकनीक के विपरीत) में काम करना जारी रखता है।
डेविड पेनिसी

0

यदि आप पृष्ठभूमि छवि को बदलने के लिए सिर्फ नेबर को उपखंड करना चाहते हैं - तो आईओएस 5 में कोई आवश्यकता नहीं है। इस एक सेट की तरह विधि होगी


1
आपको setBackgroundImage:forBarMetrics:यहां बताए अनुसार उपयोग करने की आवश्यकता है: developer.apple.com/library/IOS/#documentation/UIKit/Reference/…
आहटी

0

इसके अलावा obb64 की टिप्पणी के लिए, मैंने निब से लोड setViewControllers:animated:के rootControllerलिए नियंत्रक को सेट करने के लिए उसकी चाल का उपयोग करके समाप्त किया navigationController। यहां वह कोड है जो मैं उपयोग कर रहा हूं:

- (void) presentModalViewControllerForClass: (Class) a_class {
  UINavigationController *navController = [[[NSBundle mainBundle] loadNibNamed: @"CustomNavBar" owner:self options:nil] lastObject];

  LoginSignupBaseViewController *controller = [[a_class alloc] initWithNibName: nil bundle: nil];
  controller.navigationController = navController;
  [navController setViewControllers: A(controller) animated: NO];

  [self presentModalViewController: navController animated: YES];

  [controller release];
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.