2012-08-29 13 views
7

मैं अपने app से स्क्रीनशॉट को बचाने की जरूरत में एक CALayer रेंडर करने के लिए कैसे, तो मैं इस तरह कोड है, जो काम करता है की स्थापना की है:पृष्ठभूमि

- (void)renderScreen { 
    UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 

    CGSize outputSize = keyWindow.bounds.size; 
    UIGraphicsBeginImageContext(outputSize); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGContextSaveGState(context); 
    CALayer *layer = [keyWindow layer]; 
    [layer renderInContext:context]; 
    CGContextRestoreGState(context); 

    UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    // now save the screen image, etc... 
} 

हालांकि, जब स्क्रीन छवि जटिल (बहुत हो जाता है विचारों के), renderInContext को आईपैड 3 पर 0.8 सेकंड तक लग सकते हैं, और उस समय के दौरान यूजर इंटरफेस लॉक हो जाता है, जो कुछ अन्य कार्यक्षमताओं में हस्तक्षेप करता है।

- (void)renderScreen { 
    UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 
    CALayer *layer = [keyWindow layer]; 
    [self performSelectorInBackground:@selector(renderLayer:) withObject:layer]; 
} 

- (void)renderLayer:(CALayer *)layer { 
    UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 

    CGSize outputSize = keyWindow.bounds.size; 
    UIGraphicsBeginImageContext(outputSize); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGContextSaveGState(context); 
    [layer renderInContext:context]; 
    CGContextRestoreGState(context); 

    UIImage *screenImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 

    // now save the screen image, etc... 
} 

इंटरफ़ेस सुचारू रूप से फिर से चलाने के लिए अनुमति देता है, लेकिन कभी-कभी renderInContext लाइन पर EXC_BAD_ACCESS के साथ एक दुर्घटना का कारण बनता है कि: तो मैं एक पृष्ठभूमि धागा करने के लिए प्रतिपादन, इस तरह ले जाया गया। मैंने परत की जांच करने की कोशिश की! = शून्य और [परत प्रतिक्रियाएं चयनकर्ता: @ चयनकर्ता (renderInContext :)] पहले, इसलिए मैं दुर्घटना से बच सकता था, लेकिन दोनों स्थितियां हमेशा सच होती हैं।

फिर मैंने this SO comment पढ़ा, यह बताते हुए कि एक परत पृष्ठभूमि ऑपरेशन से पहले एक परत बदल सकती है और परत की प्रतिलिपि को पृष्ठभूमि ऑपरेशन में भेजने का सुझाव देती है। This SO answer और this one गया मुझे शुरू कर दिया है, और मैं CALayer की एक प्रति विधि जोड़ने के लिए इस श्रेणी के साथ समाप्त हो गया:

#import "QuartzCore/CALayer.h" 

@interface CALayer (CALayerCopyable) 
- (id)copy; 
@end 

@implementation CALayer (CALayerCopyable) 

- (id)copy { 
    CALayer *newLayer = [CALayer layer]; 
    newLayer.actions = [self.actions copy]; 
    newLayer.anchorPoint = self.anchorPoint; 
    newLayer.anchorPointZ = self.anchorPointZ; 
    newLayer.backgroundColor = self.backgroundColor; 
    //newLayer.backgroundFilters = [self.backgroundFilters copy]; // iOS 5+ 
    newLayer.borderColor = self.borderColor; 
    newLayer.borderWidth = self.borderWidth; 
    newLayer.bounds = self.bounds; 
    //newLayer.compositingFilter = self.compositingFilter; // iOS 5+ 
    newLayer.contents = [self.contents copy]; 
    newLayer.contentsCenter = self.contentsCenter; 
    newLayer.contentsGravity = [self.contentsGravity copy]; 
    newLayer.contentsRect = self.contentsRect; 
    //newLayer.contentsScale = self.contentsScale; // iOS 4+ 
    newLayer.cornerRadius = self.cornerRadius; 
    newLayer.delegate = self.delegate; 
    newLayer.doubleSided = self.doubleSided; 
    newLayer.edgeAntialiasingMask = self.edgeAntialiasingMask; 
    //newLayer.filters = [self.filters copy]; // iOS 5+ 
    newLayer.frame = self.frame; 
    newLayer.geometryFlipped = self.geometryFlipped; 
    newLayer.hidden = self.hidden; 
    newLayer.magnificationFilter = [self.magnificationFilter copy]; 
    newLayer.mask = [self.mask copy]; // property is another CALayer 
    newLayer.masksToBounds = self.masksToBounds; 
    newLayer.minificationFilter = [self.minificationFilter copy]; 
    newLayer.minificationFilterBias = self.minificationFilterBias; 
    newLayer.name = [self.name copy]; 
    newLayer.needsDisplayOnBoundsChange = self.needsDisplayOnBoundsChange; 
    newLayer.opacity = self.opacity; 
    newLayer.opaque = self.opaque; 
    newLayer.position = self.position; 
    newLayer.rasterizationScale = self.rasterizationScale; 
    newLayer.shadowColor = self.shadowColor; 
    newLayer.shadowOffset = self.shadowOffset; 
    newLayer.shadowOpacity = self.shadowOpacity; 
    newLayer.shadowPath = self.shadowPath; 
    newLayer.shadowRadius = self.shadowRadius; 
    newLayer.shouldRasterize = self.shouldRasterize; 
    newLayer.style = [self.style copy]; 
    //newLayer.sublayers = [self.sublayers copy]; // this line makes the screen go blank 
    newLayer.sublayerTransform = self.sublayerTransform; 
    //newLayer.superlayer = self.superlayer; // read-only 
    newLayer.transform = self.transform; 
    //newLayer.visibleRect = self.visibleRect; // read-only 
    newLayer.zPosition = self.zPosition; 
    return newLayer; 
} 

@end 

तब मैं renderScreen अद्यतन renderLayer को परत की एक प्रति भेजने के लिए:

- (void)renderScreen { 
    UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; 
    CALayer *layer = [keyWindow layer]; 
    CALayer *layerCopy = [layer copy]; 
    [self performSelectorInBackground:@selector(renderLayer:) withObject:layerCopy]; 
} 

जब मैं इस कोड को चलाता हूं, तो सभी स्क्रीन छवियां सादे सफेद होती हैं। जाहिर है मेरी प्रति विधि सही नहीं है। तो क्या कोई मुझे निम्नलिखित संभावित समाधानों में से किसी के साथ मदद कर सकता है?

  1. कैलियर के लिए एक प्रति विधि कैसे लिखें जो वास्तव में काम करता है?
  2. यह जांचने के लिए कि पृष्ठभूमि प्रक्रिया में पारित एक परत renderInContext के लिए एक वैध लक्ष्य है?
  3. इंटरफ़ेस को लॉक किए बिना जटिल परतों को प्रस्तुत करने का कोई अन्य तरीका?

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

#import "QuartzCore/CALayer.h" 

@interface CALayer (CALayerCopyable) 
- (id)copy; 
- (NSArray *)copySublayers:(NSArray *)sublayers; 
@end 

@implementation CALayer (CALayerCopyable) 

- (id)copy { 
    CALayer *newLayer = [[CALayer alloc] initWithLayer:self]; 
    newLayer.sublayers = [self copySublayers:self.sublayers]; 
    return newLayer; 
} 

- (NSArray *)copySublayers:(NSArray *)sublayers { 
    NSMutableArray *newSublayers = [NSMutableArray arrayWithCapacity:[sublayers count]]; 
    for (CALayer *sublayer in sublayers) { 
     [newSublayers addObject:[sublayer copy]]; 
    } 
    return [NSArray arrayWithArray:newSublayers]; 
} 

@end 
+2

आपका पृष्ठभूमि थ्रेड * * UIWindow' स्पर्श नहीं कर सकता है। आपको आकार को सीधे पास करने की आवश्यकता है। –

+0

ठीक है, धन्यवाद, मेरे तत्काल परीक्षण उद्देश्यों के लिए अब मैं खिड़की के आकार को कड़ी-कोडिंग कर रहा हूं। इस समाधान के आधार पर कि मैं किस समाधान के साथ समाप्त होता हूं, मैं गतिशील रूप से इसे पारित करने का एक अच्छा तरीका समझूंगा। हालांकि, यह मेरी वर्तमान समस्या को प्रभावित नहीं करता है। – arlomedia

+0

क्या आप इसके लिए एक अच्छा समाधान खत्म कर चुके हैं? –

उत्तर

2

इस प्रयोजन के लिए, मैं अपनी स्वयं की प्रतिलिपि विधि बनाने के बजाय initWithLayer: का उपयोग करेंगे। initWithLayer: स्पष्ट रूप से परतों की छाया प्रतियां बनाने के लिए है, उदाहरण के लिए, प्रेजेंटेशन लेयर विधि के लिए। "

आपको उपन्यासों की प्रतियां भी बनाने की आवश्यकता हो सकती है। मुझे तुरंत याद नहीं है कि initWithLayer: आपके लिए यह करता है। लेकिन initWithLayer: कोर एनीमेशन कैसे काम करता है, इसलिए इसे इस तरह की समस्याओं के लिए अनुकूलित किया गया है।

+0

मैंने अपनी प्रतिलिपि विधि के बजाय, initWithLayer की कोशिश की, और अभी भी एक सादे सफेद छवि के साथ समाप्त हो गया। मैंने अपनी प्रतिलिपि विधि में [कैलियर परत] की बजाय इसे भी आजमाया, और फिर सभी गुणों को पहले की तरह कॉपी किया, लेकिन फिर भी एक सादा सफेद छवि मिली। प्रलेखन कहता है "मौजूदा परत की सामग्री के साथ एक नई परत को आरंभ करने के लिए इस विधि का उपयोग न करें," इसलिए मुझे यकीन नहीं था कि यह पीछा करने योग्य था या नहीं। क्या आपको लगता है कि मुझे मूल परत के sublayers के माध्यम से पुन: प्रयास करना चाहिए और उन्हें initWithLayer के साथ नई परत में कॉपी करना चाहिए? – arlomedia

+0

मैंने सभी sublayers को कॉपी करने की कोशिश की, लेकिन अभी भी सफेद आउटपुट मिला। मैंने जिस कोड को मैंने कोशिश की है, उसके साथ मैंने प्रश्न अपडेट किया है। – arlomedia