2016-02-05 8 views
11

UIScrollViewを作成しようとすると、水平方向のズームのみが可能です。スクロールビューは、純粋な自動レイアウトアプローチで設定されています。通常のアプローチは、多くのスタックオーバーフロー回答(例えばthis one)とotherリソースで示唆されているとおりです。私はautolayoutが存在する前に、これをアプリでうまく使用しました。次のようなアプローチがある:スクロールビューのコンテンツビューで、私はsetTransform()をオーバーライドして、x方向にスケール上に変換を渡す変更:自動レイアウトを使用しているときにUIScrollViewを一方向にのみズームする方法

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     var t = newValue 
     t.d = 1.0 
     super.transform = t 
    } 
} 

私もそれはdoesnのように、スクロールビューのコンテンツは、オフセットリセット「Tは、ズーム時の垂直方向にスクロール:

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
} 

を自動レイアウトを使用しない場合これは非常にうまく動作します。

Zooming without autolayout

しかし、autolayoutを使用すると、ズーム時にコンテンツのオフセットが正しくなくなります。水平方向のコンテンツビューのみスケールが、それは垂直方向に移動します。

Zooming with autolayout

私は(この問題のビデオを作るために使用される)サンプルプロジェクトon GitHubを入れています。これには、Main.storyboardとNoAutolayout.storyboardという2つのストーリーボードが含まれていますが、Main.storyboardにはNoAutolayoutが実行されていないときにautolayoutがオンになっている点が異なります。 Main Interfaceプロジェクト設定を変更してそれらの間を切り替えると、どちらの場合でも動作を見ることができます。

マニュアルレイアウトで必要とされるよりもはるかに優れた方法でUIを実装することでいくつかの問題を解決するので、私は本当にautolayoutをオフにしません。 autolayoutを使ってズーミング中に垂直方向のコンテンツオフセットを正しい(つまりゼロ)に保つ方法はありますか?

EDIT:サンプルプロジェクトに3番目のストーリーボード、AutolayoutVariableColumns.storyboardを追加しました。これにより、テキストフィールドが追加され、GridView内の列数が変更され、内部のコンテンツサイズが変更されます。これは、この質問を促した実際のアプリで必要な動作をより詳しく示しています。

答えて

8

私はそれを考え出しました。変換中に変換を適用して、ズーム中にUIScrollViewが実行しようとするセンタリングをオフセットする必要があります。

があなたのGridViewにこれを追加します。

var unzoomedViewHeight: CGFloat? 
override func layoutSubviews() { 
    super.layoutSubviews() 
    unzoomedViewHeight = frame.size.height 
} 

override var transform: CGAffineTransform { 
    get { return super.transform } 
    set { 
     if let unzoomedViewHeight = unzoomedViewHeight { 
      var t = newValue 
      t.d = 1.0 
      t.ty = (1.0 - t.a) * unzoomedViewHeight/2 
      super.transform = t 
     } 
    } 
} 

変換を計算するために、我々は、ビューのunzoomed高さを知っておく必要があります。ここでは、layoutSubviews()中にフレームサイズを取得していて、ズームしていない高さが含まれていると仮定しています。おそらく、それが正しいわけではないいくつかのエッジケースがあります。ビューの更新サイクルで高さを計算するのが良いかもしれませんが、これが基本的な考え方です。アンナのソリューションが動作します@ものの

+0

アンナさん、ありがとうございました。興味深いアプローチですが、うまくいくようです。私はそれにショットをつけて、報告する。 –

+0

クイックテストは、これが機能することを示しているようです。私は明日の私の実際のプロジェクトにそれを統合しようとします。再度、感謝します! –

+0

あなたがそれを微調整するかどうか私に教えてください - 私は同じ問題を解決しようとしています。 (そして私の答えを受け入れることを確かめてください;-) –

0

100%自動レイアウトの要件を満たしているかどうかはわかりませんが、UIScrollViewにautolayoutが設定され、gridViewがサブビューとして追加されています。

あなたViewController.swiftは次のようになります。

// Add an outlet for the scrollView in interface builder. 
@IBOutlet weak var scrollView: UIScrollView! 

// Remove the outlet and view for gridView. 
//@IBOutlet var gridView: GridView! 

// Create the gridView here: 
var gridView: GridView! 

// Setup some views 
override func viewDidLoad() { 
    super.viewDidLoad() 
    // The width for the gridView is set to 1000 here, change if needed. 
    gridView = GridView(frame: CGRect(x: 0, y: 0, width: 1000, height: self.view.bounds.height)) 
    scrollView.addSubview(gridView) 
    scrollView.contentSize = CGSize(width: gridView.frame.width, height: scrollView.frame.height) 
    gridView.contentMode = .ScaleAspectFill 
} 

func viewForZoomingInScrollView(scrollView: UIScrollView) -> UIView? { 
    return gridView 
} 

func scrollViewDidZoom(scrollView: UIScrollView) { 
    scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0.0) 
    scrollView.contentSize = CGSize(width: scrollView.contentSize.width, height: scrollView.frame.height) 
} 
+0

素敵な答えをありがとう。残念ながら、私がautolayoutを使いたい理由は、 "GridView"(サンプルプロジェクトのGridViewほど単純ではない)は内容に基づいてサイズが変わるということです。 Autolayoutの 'intrinsicContentSize()'に対するサポートはこれには完璧です。それがなければ、内容が変化するとビューが手作業でサイズ変更されなければならず、それはかなり苦痛です。私は、コンテンツビューがその本質的なコンテンツサイズでサイズ調整できる限り、実際には_scrollビューの自動レイアウトをオフにしても構いません。 –

+0

ああ、つまらない。 'gridView'でもautolayoutを使う方法を見つけたら教えてください! – xoudini

3

は、それが動作するサンプルプロジェクトではtranslatesAutoresizingMaskIntoConstraints

func scrollViewDidZoom(scrollView: UIScrollView) { 
    gridView.translatesAutoresizingMaskIntoConstraints = true 
} 

を設定してみてください。これで十分でない場合は、scrollViewDidEndZooming:に役立つかもしれないプログラムで新しい制約を作成してみてください。これが解決しない場合、我々は、変数intrinsicContentSize()

で問題を再現することができるように

また、サンプルプロジェクトを更新してくださいオレBegemannによるこの記事では私をたくさん助けた
How I Learned to Stop Worrying and Love Cocoa Auto Layout

WWDC 2015のビデオ
Mysteries of Auto Layout, Part 1
Mysteries of Auto Layout, Part 2

幸運にも、そのための旗があります。 translatesAutoResizingMask IntoConstraints [スペースなし]と呼ばれています。

少し口が開いていますが、それはかなり言います。レガシー・レイアウト・システムではなく、オート・レイアウト・ワールドでビューを動作させます。

+0

答えをありがとう。私はこれがなぜ機能するのか完全に理解していませんが、本質的に壊れやすいハックだと思います。 'viewDidLoad()'で 'translatesAutoresizingMaskIntoConstraints'をオンにすると、すぐに競合する制約例外が発生します。いずれの場合でも、 'translatesAutoresizingMaskIntoConstraints'は、GridViewが存在するため、自動レイアウトに直接参加しているビューには使用されません。サンプルプロジェクトを変数 'intrinsicContentSize()'で更新します。 –

+0

私は、本来のコンテンツサイズに影響を与えるグリッドビューの列数を変更できるストーリーボードでサンプルプロジェクトを更新しました。 (私もプロジェクトをGitHubに移しました)。この変更により、ソリューションが分解されることに注意してください。ズームしてから列数を変更すると、自動サイズ変更マスクから変換された幅の制約によって、GridViewが新しい列数に合わせてサイズ変更されません。 –

+0

@アンナの答えは、レガシーと自動のレイアウトシステムを切り替えようとするよりも優れています。 –

1

UIScrollViewの自動レイアウトでの作業の方法を提供します。スクロールビューは他のビューとは異なる動作をしますので、しかし、制約が異なりすぎて解釈されますは、スクロールビューのコンテンツ領域に取り付けるエッジ/余白のスクロールビューのとその内容との

  • 制約を。 高さ、または中心間
  • 制約スクロールビューのフレームに接続します。
  • スクロールビューとスクロールビュー以外のビューとの間の制約は、通常のビューのように機能します。

だから、あなたはそのエッジ/マージンに固定スクロールビューにサブビューを追加するときに、そのサブビューは、スクロールビューのコンテンツ領域またはコンテンツビューになります。シーンにスクロールビューを追加

  1. AppleはWorking with Scroll Viewsで、次のアプローチを示唆しています。

  2. 通常通り、スクロールビューのサイズと位置を定義する制約を描画します。
  3. スクロールビューにビューを追加します。ビューのXcode固有のラベルをコンテンツビューに設定します。
  4. コンテンツビューの上端、下端、先頭および後端をスクロールビューの対応する端に固定します。コンテンツビューでスクロールビューのコンテンツ領域 が定義されるようになりました。
  5. (...)
  6. (...)
  7. コンテンツビュー内のスクロールビューのコンテンツをレイアウト。制約を使用して、コンテンツビュー内のコンテンツを通常の位置に配置します。あなたのケースでは

GridViewのコンテンツビュー内でなければなりません、あなたは今、使用してされているが、グリッドビューの制約を維持することができます

Interface Builder

コンテンツビューに添付されます。また、水平方向にのみズームするには、そのままコードをそのまま使用してください。あなたのtransformオーバーライドは非常にうまくそれを処理します。

+0

私はこれをすべて理解しています。質問は、水平のみのスクロールとはまったく異なる獣である、水平のみの_zooming_についてです。この質問に触発された実際のアプリでは、_scrolling_は両方向で動作し、コンテンツビューの高さは可変です。それは、水平方向に制約されるべきズームのみです。 –

+0

問題ありません。水平スクロールはちょうど推測でした。水平方向のズームについて、あなたはすでにそれを解決しました。残っているのは、自動レイアウトで水平方向のズームを解決することです。このソリューションでは、アンナなしで行うことができます。私はテストし、それは正常に働いた。不明な点がある場合は、教えてください。投稿を編集します。 –

+0

Annaのソリューションは、純粋な自動レイアウト手法を使用して水平方向のズームを行う場合に非常に有効です。あなたが別のアプローチをしているなら、私はそれについて聞くことに興味があるでしょう。ここでのあなたの答えはズーミングについて言及していません。 –

関連する問題