2013-03-31 6 views
16

मेरे पास एक कस्टम ऑब्जेक्ट है, यह एनएसओब्जेक्ट से प्राप्त होता है। यह ऑब्जेक्ट "कुछ चीजें" करता है, इसमें से एक कुछ UIKit ऑब्जेक्ट्स (UILabel, UIButtons ecc ecc ...) के साथ एक UIView बना रहा है। इस ऑब्जेक्ट में कुछ गुण हैं जैसे: टेक्स्टकॉलर, फ़ॉन्ट, पृष्ठभूमि रंग ... जिसका उपयोग निहित UIKit ऑब्जेक्ट्स की उपस्थिति को अनुकूलित करने के लिए किया जाता है।कस्टम ऑब्जेक्ट्स के लिए यूआईपीपेरेंस प्रॉक्सी

मैं इस ऑब्जेक्ट के सभी बनाए गए उदाहरणों के लिए इस गुण को "एक शॉट" कस्टमाइज़ करना चाहता हूं, और मैंने UIAppearance प्रोटोकॉल को देखा है।

मानक UIKit ऑब्जेक्ट्स पहले से ही यूआईपीपेरेंस प्रोटोकॉल के अनुरूप हैं, लेकिन मैं सभी UILabels या UIButtons पर शैली को लागू नहीं करना चाहता हूं। मैं अपने ऑब्जेक्ट इंस्टेंस के अंदर केवल UILabels और UIButtons में शैलियों को लागू करना चाहता हूं। इसके अलावा, मैं उपस्थिति का उपयोग नहीं कर सकता (और मैं नहीं चाहता) उपस्थिति का उपयोग करें: क्योंकि मेरे कस्टम ऑब्जेक्ट का उपयोग करने वाला डेवलपर यह नहीं जानता कि इसके अंदर किस प्रकार की वस्तुएं "निहित" हैं।

तो, मैं देख रहा था कि मेरी कस्टम ऑब्जेक्ट को यूआईपीपीरेंस प्रोटोकॉल के अनुरूप कैसे बनाया जाए।

AFAIK यह

+ (id)appearance 

विधि को लागू करना चाहिए। इस विधि को प्रॉक्सी ऑब्जेक्ट वापस करना चाहिए जहां आप अपने सभी अनुकूलन भेज सकते हैं। लेकिन, UIKit ऑब्जेक्ट्स की उपस्थिति विधि को देखते हुए, मुझे लगता है कि एक निजी वस्तु वापस आती है। कक्षा _UIAppearance का एक वस्तु।

तो ऐसा लगता है कि ऐप्पल मुझे अपना खुद का अनुकूलन करने के लिए मानक प्रॉक्सी ऑब्जेक्ट नहीं देता है, और मुझे स्क्रैच से बनाना है। क्या यह सही है या मैं कुछ खो रहा हूं?

धन्यवाद

उत्तर

14

कुछ पुनर्विक्रय के बाद मैं एक मानक ऐप्पल ऑब्जेक्ट का उपयोग करने के बारे में "छोड़ देता हूं"। यह अभी के लिए मौजूद नहीं है। मैंने अपनी खुद की प्रॉक्सी बनाई है, यह काफी सरल है (अब तक "उपस्थिति:" के साथ काम करता है)।

चलिए इसे समझाएं। मैं एनएसओब्जेक्ट सबक्लास पर "टेक्स्टकॉलर" की उपस्थिति सेट करना चाहता हूं, चलिए इसे "फ्लॉब्जेक्ट" कहते हैं। FLObject UIAppearance प्रोटोकॉल के अनुरूप है और उपस्थिति विधि को ओवरराइड करें।

+ (id)appearance 
{ 
    return [FLAppearance appearanceForClass:[self class]]; 
} 

यह कैसे काम करता: इस विधि में, आप एक प्रॉक्सी वर्ग (एक मैं बनाया) लौट जाना चाहिए? Flapepearance उपस्थिति द्वारा पारित प्रत्येक वर्ग के लिए स्वयं का एक उदाहरण बनाता है ForClass: विधि। यदि आप इसे एक ही कक्षा के लिए दो बार कहते हैं, तो वही उदाहरण लौटा दिया जाता है।

उसके बाद, आप कुछ इस तरह कर सकते हैं:, विधि तो यह भेजे गए सभी तरीकों स्वीकार करता है:

[[FLObject appearance] setTextColor:[UIColor redColor]]; 

FLAppearance ओवरराइड करता है forwardInvocation। फिर, यह सभी आमंत्रणों को एक सरणी में रखता है। जब FLObject आरंभ नहीं हो जाता,

[(FLAppearance *)[FLAppearance appearanceForClass:[self class]] startForwarding:self]; 

के लिए एक सरल कॉल आमंत्रण भेजने के लिए और उपस्थिति स्थापित करने के लिए शुरू कर देंगे। निश्चित रूप से, इसे कुछ ट्यूनिंग और त्रुटि जांच की आवश्यकता है, लेकिन मुझे लगता है कि यह एक अच्छी शुरुआत है।

@interface FLAppearance() 

@property (strong, nonatomic) Class mainClass; 
@property (strong, nonatomic) NSMutableArray *invocations; 

@end 

static NSMutableDictionary *dictionaryOfClasses = nil; 

@implementation FLAppearance 

// this method return the same object instance for each different class 
+ (id) appearanceForClass:(Class)thisClass 
{ 
    // create the dictionary if not exists 
    // use a dispatch to avoid problems in case of concurrent calls 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
     if (!dictionaryOfClasses) 
      dictionaryOfClasses = [[NSMutableDictionary alloc]init]; 
    }); 



    if (![dictionaryOfClasses objectForKey:NSStringFromClass(thisClass)]) 
    { 
     id thisAppearance = [[self alloc]initWithClass:thisClass]; 
     [dictionaryOfClasses setObject:thisAppearance forKey:NSStringFromClass(thisClass)]; 
     return thisAppearance; 
    } 
    else 
     return [dictionaryOfClasses objectForKey:NSStringFromClass(thisClass)]; 
} 

- (id)initWithClass:(Class)thisClass 
{ 
    self = [self initPrivate]; 
    if (self) { 
     self.mainClass = thisClass; 
     self.invocations = [NSMutableArray array]; 
    } 
    return self; 
} 

- (id)init 
{ 
    [NSException exceptionWithName:@"InvalidOperation" reason:@"Cannot invoke init. Use appearanceForClass: method" userInfo:nil]; 
    return nil; 
} 

- (id)initPrivate 
{ 
    if (self = [super init]) { 

    } 
    return self; 
} 

-(void)forwardInvocation:(NSInvocation *)anInvocation; 
{ 
    // tell the invocation to retain arguments 
    [anInvocation retainArguments]; 

    // add the invocation to the array 
    [self.invocations addObject:anInvocation]; 
} 

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 
    return [self.mainClass instanceMethodSignatureForSelector:aSelector]; 
} 

-(void)startForwarding:(id)sender 
{ 
    for (NSInvocation *invocation in self.invocations) { 
     [invocation setTarget:sender]; 
     [invocation invoke]; 
    } 
} 
+0

ठीक काम करता है मैंने इसे बढ़ाया है, इसलिए यह कक्षा हिरासत में भी यात्रा करेगा ताकि यह अभिभावक वर्ग के विकल्प भी सेट कर सके: https://gist.github.com/vkodocha/5500276 (कोड इनलाइन जोड़ने में लंबा है)। – Chris

+0

अच्छा काम! एक सुधार के रूप में, आप 'id' के बदले' instancetype' का उपयोग रिटर्न प्रकार के रूप में कर सकते हैं, ताकि आप स्पष्ट कलाकार को 'फ्लैपरपेरेंस' पर भी छोड़ सकें। –

+0

@ गैब्रिएल पेट्रोनेला संकेत के लिए धन्यवाद ;-) – LombaX

0

बाहर http://logicalthought.co/blog/2012/10/8/uiappearance-and-custom-views

मूल रूप से आप सिर्फ UI_APPEARANCE_SELECTOR के साथ अपने गुण टैग करने की जरूरत है और सब कुछ जब तक आपके वर्ग UIView का एक उपवर्ग जो निजी _UIAppearance वर्ग की वास्तविक वेंडिंग संभाल लेंगे है काम करता है की जाँच करें ।


संपादित करें:

आप शायद बंद बस अपनी स्वयं की और समाधान एक सिंगलटन का उपयोग कर कुछ वर्ग तरीकों रोलिंग के बजाय कुछ क्रम के साथ डरावना क्या करने की कोशिश बेहतर कर रहे हैं। ऐसा लगता है कि UIAppearance आपके उपयोग के मामले का समर्थन करता है।

दूसरी तरफ, आप प्रत्येक ऑब्जेक्ट को निजी UIView सबक्लास में वेंड कर सकते हैं और उसके बाद उस सबक्लास के उदाहरणों को वेंड कर सकते हैं। फिर आप अपने NSObject पर भेजे गए उपस्थिति संदेशों को अग्रेषित करने के उदाहरण भेज सकते हैं और appearanceWhenContainedIn:<your private subclass> का उपयोग कर सकते हैं। हालांकि यह गड़बड़ हो सकता है और आपकी कक्षा के उपभोक्ताओं के लिए भ्रमित हो सकता है।

+0

धन्यवाद, मुझे पता है कि UIView उपclasses में इसका उपयोग कैसे करें। जैसा कि मैंने निर्दिष्ट किया है, मैं एनएसओब्जेक्ट सबक्लास – LombaX

+0

में यूआईपीपीरेंस लागू करना चाहता हूं, मेरे संपादन देखें। क्या उससे किसी भी तरह से सहायता मिलती है? –

+0

मैंने यूआईपीपीरेंस प्रोटोकॉल के साथ अपनी खुद की प्रॉक्सी को संगत बनाने का चयन किया है, यह लगता है कि यह आसान है :-) मैंने कोड को एक उत्तर में जोड़ा है। – LombaX

2

अच्छा कार्यान्वयन, मैं थोड़ा कोड को संशोधित किया और NSProxy का एक उपवर्ग के रूप में वर्ग बनाया। एक प्रोजेक्ट में इसका उपयोग करके मुझे एक मेमोरी लीक मिला:

उदाहरण के लिए: वैश्विक सेटिंग्स/उपस्थिति सेट करने के लिए प्रॉक्सी का उपयोग करके, उस वर्ग का प्रत्येक उदाहरण कभी भी refCount 0 तक नहीं पहुंच पाएगा, इसलिए dealloc कभी नहीं कहा जाएगा।

रिसाव कोड:

-(void)forwardInvocation:(NSInvocation *)anInvocation; 
{ 
    [...] 

    // !! This will retain also the target 

    [anInvocation retainArguments]; 

    [...] 
} 

फिक्स: NSInvocation

के लिए

-(void)forwardInvocation:(NSInvocation *)anInvocation 
{ 
    [anInvocation setTarget:nil]; 
    [anInvocation retainArguments]; 

    // add the invocation to the array 
    [self.invocations addObject:anInvocation]; 
} 

-(void)startForwarding:(id)sender 
{ 
    for (NSInvocation *invocation in self.invocations) { 

     // Create a new copy of the stored invocation, 
     // otherwise setting the new target, this will never be released 
     // because the invocation in the array is still alive after the call 

     NSInvocation *targetInvocation = [invocation copy]; 
     [targetInvocation setTarget:sender]; 
     [targetInvocation invoke]; 
     targetInvocation = nil; 
    } 
} 

कॉपी श्रेणी

-(id)copy 
{ 
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignature]]; 
    NSUInteger numberOfArguments = [[self methodSignature] numberOfArguments]; 

    [invocation setTarget:self.target]; 
    [invocation setSelector:self.selector]; 

    if (numberOfArguments > 2) { 
     for (int i = 0; i < (numberOfArguments - 2); i++) { 
      char buffer[sizeof(intmax_t)]; 
      [self getArgument:(void *)&buffer atIndex:i + 2]; 
      [invocation setArgument:(void *)&buffer atIndex:i + 2]; 
     } 
    } 

    return invocation; 
} 
+1

उत्तर प्रारंभ में छोटी गलती है अग्रेषण, यह होना चाहिए: एनएसआईएनवोकेशन * targetInvocation = [आमंत्रण प्रतिलिपि]; [targetInvocation setTarget: प्रेषक]; [targetInvocation invoke]; targetInvocation = nil; – mientus

4

अपने ही परियोजना के प्रयोजनों के लिए, मैं सब कुछ एक साथ और जारी की कस्टम UIApperance इकट्ठा ओपन सोर्स प्रोजेक्ट के रूप में प्रॉक्सी MZApperance

संबंधित मुद्दे