2009-01-15 16 views
15

मेरे पास UIScrollView के अंदर एक ग्राफ खींचा जा रहा है। CATiledLayer के कस्टम उप-वर्ग का उपयोग करके इसकी परत के रूप में यह एक बड़ा UIView है।UIScrollView ज़ूम के बाद मैं रीसेट कैसे करूं?

जब मैं UIScrollView में ज़ूम इन और आउट करता हूं, तो मैं ग्राफ को viewForZoomingInScrollView से ग्राफ़ वापस करने पर गतिशील रूप से आकार बदलना चाहता हूं। हालांकि, ग्राफ़ नए ज़ूम स्तर पर खुद को फिर से चलाता है, और मैं ट्रांसफॉर्म स्केल को 1x1 पर रीसेट करना चाहता हूं ताकि अगली बार उपयोगकर्ता ज़ूम कर सके, ट्रांसफॉर्म वर्तमान दृश्य से शुरू होता है। अगर मैं ट्रांसफॉर्म को scrollViewDidEndZooming में पहचान में रीसेट कर देता हूं, तो यह सिम्युलेटर में काम करता है, लेकिन डिवाइस पर EXC_BAD_ACCSES फेंकता है।

यह सिम्युलेटर पर पूरी तरह से समस्या को हल नहीं करता है, क्योंकि अगली बार जब उपयोगकर्ता ज़ूम करता है, तो ट्रांसफॉर्म स्वयं को जो भी ज़ूम स्तर पर रीसेट करता है, और ऐसा लगता है, अगर मैं 2x पर ज़ूम किया गया था , उदाहरण के लिए, यह अचानक 4x पर है। जब मैं ज़ूम खत्म करता हूं, तो यह सही पैमाने पर समाप्त होता है, लेकिन ज़ूमिंग का वास्तविक कार्य खराब दिखता है।

तो सबसे पहले: ज़ूमिंग के बाद ग्राफ को 1x1 के मानक पैमाने पर फिर से चलाने की अनुमति कैसे दे सकता हूं, और मेरे पास एक चिकनी ज़ूम कैसे हो सकता है?

संपादित करें: नई खोजों त्रुटि हो "[CALayer retainCount]: message sent to deallocated instance"

मैं कभी नहीं किसी भी परतों अपने आप deallocating कर रहा हूँ लगता है। इससे पहले, मैं किसी भी विचार या कुछ भी हटा नहीं रहा था। यह त्रुटि ज़ूम पर और घूमने पर भी फेंक दी गई थी। अगर मैं रोटेशन से पहले ऑब्जेक्ट को हटा देता हूं और बाद में इसे फिर से जोड़ता हूं, तो यह अपवाद फेंक नहीं देता है। यह ज़ूम करने का विकल्प नहीं है।

उत्तर

41

मैं आपको यह जांचने के अलावा दुर्घटनाग्रस्त होने में मदद नहीं कर सकता हूं, यह सुनिश्चित करने के अलावा कि आप अनजाने में अपने कोड के भीतर किसी दृश्य या परत को स्वचालित रूप से स्वत: नहीं कर रहे हैं। मैंने सिम्युलेटर को डिवाइस पर अलग-अलग ऑटोरेलीज़ के समय को संभालने में देखा है (अक्सर जब धागे शामिल होते हैं)।

व्यू स्केलिंग UIScrollView के साथ एक समस्या है, हालांकि मैंने भाग लिया है। एक चुटकी-ज़ूमिंग घटना के दौरान, UIScrollView आपके द्वारा viewForZoomingInScrollView: प्रतिनिधि विधि में निर्दिष्ट दृश्य ले जाएगा और इसमें एक परिवर्तन लागू करेगा। यह परिवर्तन प्रत्येक फ्रेम को फिर से निकाले बिना दृश्य की एक चिकनी स्केलिंग प्रदान करता है। ज़ूम ऑपरेशन के अंत में, आपके प्रतिनिधि विधि scrollViewDidEndZooming:withView:atScale: को कॉल किया जाएगा और आपको नए पैमाने पर कारक पर आपके दृश्य की उच्च गुणवत्ता वाले प्रतिपादन करने का मौका मिलेगा। आम तौर पर, यह सुझाव दिया जाता है कि आप अपने दृश्य पर ट्रांसफॉर्म को CGAffineTransformIdentity पर रीसेट करें और फिर अपना दृश्य मैन्युअल रूप से नए आकार के पैमाने पर फिर से खींचा जाए।

हालांकि, यह समस्या का कारण बनता है क्योंकि UIScrollView सामग्री दृश्य परिवर्तन की निगरानी करने के लिए प्रतीत नहीं होता है, इसलिए अगले ज़ूम ऑपरेशन पर यह सामग्री दृश्य का परिवर्तन जो भी समग्र पैमाने कारक है, को बदल देता है। चूंकि आपने अंतिम पैमाने पर कारक को मैन्युअल रूप से फिर से खींचा है, यह स्केलिंग को जोड़ता है, जो आप देख रहे हैं।

समाधान के लिए, मैं एक UIView उपवर्ग मेरी सामग्री देखने के लिए निम्न विधियों में परिभाषित के साथ उपयोग करें:

- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform; 
{ 
    [super setTransform:newTransform]; 
} 

- (void)setTransform:(CGAffineTransform)newValue; 
{ 
    [super setTransform:CGAffineTransformScale(newValue, 1.0f/previousScale, 1.0f/previousScale)]; 
} 

जहां previousScale देखने के एक नाव उदाहरण चर रहा है। मैं तो जूम करने प्रतिनिधि विधि लागू इस प्रकार है:

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale; 
{ 
    [contentView setTransformWithoutScaling:CGAffineTransformIdentity]; 
// Code to manually redraw view at new scale here 
    contentView.previousScale = scale; 
    scrollView.contentSize = contentView.frame.size; 
} 

ऐसा करने से, सामग्री को देखने के लिए भेजा रूपांतरण पैमाने जिस पर दृश्य पिछले दोबारा बनाई थी के आधार पर समायोजित कर रहे हैं।जब चुटकी-ज़ूमिंग की जाती है, तो सामान्य setTransform: विधि में समायोजन को बाईपास करके ट्रांसफॉर्म 1.0 के पैमाने पर रीसेट किया जाता है। ज़ूम के पूरा होने पर आपको एक कुरकुरा दृश्य खींचने के दौरान सही स्केलिंग व्यवहार प्रदान करना प्रतीत होता है।

अद्यतन (7/23/2010): आईफोन ओएस 3.2 और ऊपर ज़ूमिंग के संबंध में स्क्रॉल दृश्यों का व्यवहार बदल गया है। अब, UIScrollView आपके द्वारा सामग्री दृश्य पर लागू पहचान परिवर्तन का सम्मान करेगा और केवल -scrollViewDidEndZooming:withView:atScale: में सापेक्ष स्केल कारक प्रदान करेगा। इसलिए, UIView सबक्लास के लिए उपरोक्त कोड 3.2 से पुराने पुराने आईफोन ओएस संस्करण चलाने वाले उपकरणों के लिए आवश्यक है।

+2

आपको थोड़ा और विशिष्ट होना पड़ सकता है। कौन सी त्रुटियां? मैं लगभग एक आवेदन में इस तरह कोड का उपयोग करता हूं, और यह ठीक काम करता है। –

+0

सामग्री दृश्य गायब क्यों रहता है? – domlao

+1

आपने अधिक जानकारी प्रदान नहीं की है, लेकिन यदि आप पदानुक्रम से सामग्री दृश्य को मैन्युअल रूप से छिपा रहे या हटा नहीं रहे हैं, तो मेरा अनुमान है कि यह GPU द्वारा समर्थित बनावट आकार से बड़ा हो रहा है। यदि दृश्य 2048 पिक्सेल से बड़ा या लंबा हो जाता है, तो इसे अब आईफोन और आईफोन 3 जी पर प्रस्तुत नहीं किया जा सकता है (मैं 3 जी एस के लिए बात नहीं कर सकता)। –

5

मेरे पास विस्तृत जानकारी है कि कैसे (और क्यों) UIScrollView ज़ूमिंग github.com/andreyvit/ScrollingMadness/ पर काम करता है।

(लिंक भी प्रोग्राम रूप UIScrollView ज़ूम करने के लिए का एक विवरण होता है, कैसे अनुकरण करने के लिए फोटो लाइब्रेरी शैली पेजिंग + जूमिंग स्क्रॉल, एक उदाहरण परियोजना और ZoomScrollView वर्ग कि जूमिंग जादू के कुछ समाहित +।)

उद्धरण:

UIScrollView में "वर्तमान ज़ूम स्तर" की धारणा नहीं है, क्योंकि इसमें प्रत्येक सबव्यूव में अपना वर्तमान ज़ूम स्तर हो सकता है। ध्यान दें कि वर्तमान ज़ूम स्तर को रखने के लिए UIScrollView में कोई फ़ील्ड नहीं है। हालांकि हम जानते हैं कि कोई ज़ूम स्तर संग्रहीत करता है, क्योंकि यदि आप एक सबव्यूव चुपचाप ज़ूम करते हैं, तो इसके परिवर्तन को CGAffineTransformIdentity पर रीसेट करें, और फिर दोबारा चुटकी लें, आप देखेंगे कि सबव्यू के पिछले ज़ूम स्तर को पुनर्स्थापित कर दिया गया है।

वास्तव में, यदि आप डिस्सेप्लिब्स को देखते हैं, तो यह यूआईवीव है जो अपने ज़ूम स्तर को संग्रहीत करता है (UIGestureInfo ऑब्जेक्ट के अंदर _gestureInfo फ़ील्ड द्वारा इंगित)। इसमें zoomScale और setZoomScale:animated: जैसी अच्छी अनियंत्रित विधियों का एक सेट भी है। (आपको याद है, इसमें घूर्णन से संबंधित तरीकों का भी गुच्छा है, शायद हमें जल्द ही कुछ दिन रोटेशन इशारा समर्थन मिल रहा है।)

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

+0

ज़ूमस्केल UISiewrollView का हिस्सा है, UIView नहीं। – Jonny

+0

मुझे लगता है कि अब आपने अपनी स्क्रॉलिंग पागलपन सामग्री को स्थानांतरित कर दिया है? क्या आप लिंक को अपडेट करने में सक्षम होंगे (मैंने नया घर नहीं ढूंढ पाया है)। – Benjohn

+1

@ बेनोज़न हे, यह निराशाजनक रूप से कई साल पहले निराश हो गया था। लेकिन अब आप UIScrollView पर ज़ूमस्केल को रीसेट कर सकते हैं, इसलिए इस ट्रिकरी की अब आवश्यकता नहीं है (आईओएस 3 या 4 के बाद से)। –

2

ध्यान दें कि ब्रैड द्वारा प्रदान किए गए समाधान में एक समस्या है हालांकि: यदि आप प्रोग्रामेटिक रूप से ज़ूम इन करते हैं (चलिए दो बार टैप ईवेंट पर कहते हैं) और उपयोगकर्ता को मैन्युअल रूप से (चुटकी आउट) ज़ूम आउट करने दें, कुछ समय बाद सत्य के बीच अंतर स्केल और स्केल UIScrollView ट्रैकिंग बहुत बड़ी हो जाएगी, इसलिए previousScale किसी बिंदु पर फ्लोट की परिशुद्धता से बाहर निकल जाएगा जो अंततः एक अप्रत्याशित व्यवहार

3

आईओएस 3.2 और 4.0 के लिए उपयुक्त एक काफी विश्वसनीय दृष्टिकोण है, इस प्रकार है। आपको किसी भी अनुरोधित पैमाने पर अपने स्केलेबल व्यू (स्क्रॉल व्यू का मुख्य सबव्यू) प्रदान करने के लिए तैयार रहना चाहिए। फिर scrollViewDidEndZooming: में आप इस दृश्य के धुंधले पैमाने पर परिवर्तित संस्करण को हटा देंगे और इसे नए पैमाने पर खींचे गए नए दृश्य के साथ बदल देंगे।

आप की आवश्यकता होगी:

  • पिछले पैमाने बनाए रखने के लिए एक इवर; आइए इसे पुराना स्केल करें

  • viewForZoomingInScrollView: और scrollViewDidEndZooming: दोनों के लिए स्केलेबल दृश्य की पहचान करने का एक तरीका; यहां, मैं एक टैग लागू करने जा रहा हूं (99 9)

  • एक विधि जो स्केलेबल दृश्य बनाता है, इसे टैग करती है, और इसे स्क्रॉल व्यू में डालती है (यहां मैं इसे addNewScalableViewAtScale: कहूंगा)।याद रखें, इसे स्केल के अनुसार सबकुछ करना चाहिए - यह दृश्य और इसके सबव्यूज़ या ड्राइंग को आकार देता है, और स्क्रॉल व्यू की सामग्री को आकार देता है मिलान करने के लिए आकार।

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

बहुत अच्छी तरह से। जब आप स्क्रॉल व्यू बनाते हैं, तो आप स्केल 1.0 पर स्केलेबल दृश्य डालते हैं। यह आपके viewDidLoad में हो सकता है, उदाहरण के लिए (sv स्क्रॉल दृश्य है):

[self addNewScalableViewAtScale: 1.0]; 
sv.minimumZoomScale = MIN; 
sv.maximumZoomScale = MAX; 
self->oldScale = 1.0; 
sv.delegate = self; 

फिर अपनी scrollViewDidEndZooming: आप एक ही बात करते हैं, लेकिन पैमाने में बदलाव के लिए मुआवजा:

UIView* v = [sv viewWithTag:999]; 
[v removeFromSuperview]; 
CGFloat newscale = scale * self->oldScale; 
self->oldScale = newscale; 
[self addNewScalableViewAtScale:newscale]; 
sv.minimumZoomScale = MIN/newscale; 
sv.maximumZoomScale = MAX/newscale; 
12

धन्यवाद पिछले सभी उत्तरों के लिए, और यहां मेरा समाधान है।
लागू UIScrollViewDelegate तरीके: - आउटलेट

  • सेट maximumZoomScale और minimumZoomScale UIScrollView करने
  • से पहले

    - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView 
    { 
        return tmv; 
    } 
    
    - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale 
    { 
        CGPoint contentOffset = [tmvScrollView contentOffset]; 
        CGSize contentSize = [tmvScrollView contentSize]; 
        CGSize containerSize = [tmv frame].size; 
    
        tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale/scale; 
        tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale/scale; 
    
        previousScale *= scale; 
    
        [tmvScrollView setZoomScale:1.0f]; 
    
        [tmvScrollView setContentOffset:contentOffset]; 
        [tmvScrollView setContentSize:contentSize]; 
        [tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)]; 
    
        [tmv reloadData]; 
    } 
    
    • TMV UIView
    • tmvScrollView की मेरी उपवर्ग है
    • previousScale उदाहरण चर बना सकते हैं और पूरी तरह से मेरे लिए करने के लिए 1.0f

    वर्क्स अपने मूल्य निर्धारित किया है।

    बीआर, यूजीन।

    +0

    मुझे इसी तरह की स्थिति का सामना करना पड़ा। आपके समाधान ने मेरी समस्या हल की, धन्यवाद। +1 प्रतिनिधि आपके पास जाता है :) – fyasar

    2

    मैं बस लोड ज़ूम स्केल पर लोड पर रीसेट करना चाहता था, क्योंकि जब मैं इसे ज़ूम करता हूं, तो स्क्रॉलव्यू में अगली बार जब तक इसे हटाया नहीं जाता है तब तक ज़ूम स्केल होता है।

    -(void) viewWillAppear:(BOOL)animated { 
         [self.scrollView setZoomScale:1.0f]; 
    } 
    

    खेल बदल गया।

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

    • कोई संबंधित समस्या नहीं^_^