2009-01-15 56 views
15

我在UIScrollView中繪製了一張圖。這是一個大的UIView使用CATiledLayer的自定義子類作爲其圖層。如何在UIScrollView縮放後重置?

當我放大和縮小UIScrollView時,我想讓圖形動態調整大小,就像我從viewForZoomingInScrollView返回圖形時一樣。但是,圖形會以新的縮放級別重新繪製自己,並且我想將變換比例重置爲1x1,以便下次用戶放大時,變換將從當前視圖開始。如果我在scrollViewDidEndZooming中將變換重置爲標識,它將在模擬器中運行,但會在設備上引發EXC_BAD_ACCSES

這甚至不能完全在模擬器上解決問題,因爲下次用戶放大時,轉換會將其自身重置爲它所處的任何縮放級別,所以看起來像是,如果我放大到2x ,例如,它突然在4倍。當我完成縮放時,它會以正確的比例結束,但縮放的實際行爲看起來很糟糕。

所以首先:如何讓圖形在縮放後以1x1的標準比例重繪自己,以及如何在整個過程中實現平滑縮放?

編輯:新發現 的錯誤似乎是「[CALayer retainCount]: message sent to deallocated instance

,我從來沒有任何重新分配自己的層。之前,我甚至沒有刪除任何視圖或任何東西。這個錯誤是在縮放和旋轉時引發的。如果我在旋轉之前刪除對象並在之後重新添加它,它不會拋出異常。這不是縮放的選項。

回答

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的縮放比例。這似乎提供了正確的縮放行爲,同時讓您在完成縮放時繪製清晰的視圖。

UPDATE(7/23/2010):iPhone OS 3.2及以上版本已經改變了滾動視圖在縮放方面的行爲。現在,UIScrollView將尊重您應用於內容視圖的身份轉換,並僅在-scrollViewDidEndZooming:withView:atScale:中提供相對比例因子。因此,對於運行iPhone 3.2以上版本的設備,僅需要UIView子類的上述代碼。

+2

您可能需要更具體些。哪些錯誤?我在應用程序中幾乎完全像這樣使用代碼,它在那裏工作得很好。 – 2009-07-10 12:17:25

+0

爲什麼內容視圖不斷消失? – domlao 2009-12-06 16:56:36

+1

您還沒有提供太多信息,但是如果您沒有手動隱藏或從層次結構中刪除內容視圖,我的猜測是它正在變得比GPU支持的紋理大小更大。如果視圖變得比2048像素更寬或更高,它不能再在iPhone和iPhone 3G上呈現(我不能說3GS)。 – 2009-12-06 20:14:56

5

我有詳細的討論如何(以及爲什麼)UIScrollView縮放工作在github.com/andreyvit/ScrollingMadness/

(該鏈接還包含了如何編程方式放大UIScrollView的描述,如何效仿圖片庫式傳呼+變焦+滾動,實例項目和ZoomScrollView類,封裝了一些變焦魔法。)

Quote:

UIScrollView沒有「當前縮放級別」的概念,因爲它包含的每個子視圖可能有其自己的當前縮放級別。請注意,UIScrollView中沒有字段保持當前縮放級別。但是我們知道有人存儲縮放級別,因爲如果您捏住縮放子視圖,然後將其轉換重置爲CGAffineTransformIdentity,然後再次捏住,您會注意到子視圖的前一縮放級別已恢復。事實上,如果你看看反彙編,它是UIView存儲它自己的縮放級別(在_gestureInfo字段指向的UIGestureInfo對象內部)。它還有一套很好的無證方法,如zoomScalesetZoomScale:animated:。 (請注意,它也有一些旋轉相關的方法,也許我們很快就會得到旋轉手勢支持。)

但是,如果我們創建一個新的UIView,只是爲了縮放並添加我們真正的可縮放視圖爲它的孩子,我們將始終從縮放級別1.0開始。我的程序縮放實現是基於這個技巧。

2

請注意,由Brad提供的解決方案有一個問題,但:如果您保持編程縮放(讓我們說雙擊事件),並讓用戶手動(捏出)縮小,經過一段時間的真正規模和規模UIScrollView正在跟蹤將變得太大,所以previousScale將在某些時候浮出水面的精度,這將最終導致不可預知的行爲

3

一個相當可靠的方法,適用於iOS 3.2和4.0及更高版本,如下。您必須準備好以任何請求的比例提供可縮放視圖(滾動視圖的主要子視圖)。然後在scrollViewDidEndZooming:中,您將刪除該視圖的模糊比例轉換版本,並將其替換爲繪製到新比例的新視圖。

你將需要:

  • 維持以前的規模伊娃;我們稱之爲oldScale

  • 一種標識可擴展視圖的方法,用於viewForZoomingInScrollView:scrollViewDidEndZooming:;在這裏,我將應用一個標籤(999)

  • 一種創建可縮放視圖,爲其添加標籤並將其插入滾動視圖(這裏我將其稱爲addNewScalableViewAtScale:)的方法。請記住,它必須根據比例來執行所有操作 - 它調整視圖及其子視圖或繪圖的大小,並調整滾動視圖的contentSize的大小以匹配。

  • minimumZoomScale和maximumZoomScale的常量。這些都是需要的,因爲當我們用詳細的較大版本替換縮放視圖時,系統會認爲當前的比例是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方法:

- (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的子類 - 出口的UIScrollView
  • 設置maximumZoomScaleminimumZoomScale
  • 0123之前
  • 創建previousScale實例變量,並將其值設置完全以1.0F

爲我工作。

BR,Eugene。

2

我只是想重置加載到默認縮放比例,因爲當我縮放它時,滾動視圖下一次具有相同的縮放比例,直到它被取消分配。

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

改變了遊戲。