web-dev-qa-db-ja.com

iPhoneとiPadで自動レイアウトの左マージンが異なるUITableViewCell

オプション画面/シーン用に静的セルでグループ化されたUITableViewを使用しています。すべては、Xcode 6.1/iOS 8.1.x/Autolayoutを使用したStoryboardで行われます。テーブルグループ内には、さまざまなタイプのセルがあり、問題を引き起こす2つのタイプがあります。

  1. カスタムスタイルのセルと
  2. スタイルが「右詳細」のセル

セル#1で、ラベルと先頭コンテナの間に左マージンの制約を設定できます。セル#2では、私が知る限り、Interface Builderで制約を設定できません。セル#1のラベルに左マージンを設定して、セル#2のラベルと揃えるようにしました。 iPhoneではすべてが正常に見えますが、Table Viewのコンテナーのサイズが画面サイズの半分であるiPadで同じテーブルを表示すると、セル#1は絶対マージンを維持しながら、セル#2のマージンは(動的に?)制約で設定します。また、セル#1の左マージンを「relative to margin」属性で変更しようとしましたが、効果がありません。

iPhone:

Table on iPhone

iPad(テーブルビュー幅=画面サイズ1/2)

Table on iPad

質問は次のとおりです。セル#2のように整列するようにセル#1のラベルに制約を設定するにはどうすればよいですか。

また、問題を示すXcode 6.1サンプルプロジェクトへのリンクもあります。 iPhoneとiPadで実行して、違いを確認します。

https://dl.dropboxusercontent.com/u/5252156/Code/tableViewTest.Zip

この質問は iPhoneおよびiPadの静的テーブルセルのレイアウト に関連している可能性がありますが、すべてが適応型になっているため、iOS 8でも異なる場合があります。とにかくこの質問を投稿することにしました。

59
Georg

修正方法

Apple多くのサンプルプロジェクトとスクリーンショットを含むバグ報告チームと戦い、 その答え を分析した後、カスタムスタイルのセルを動作させるソリューションが見つかりました。一貫してマージンに関して、デフォルトのUITableViewCellsと同じように、次のことを行う必要があります(主に Beckyの答え に基づいて、何が違うのか、それが私にとってうまくいったのかを強調しました):

  1. IBでセルのコンテンツビューを選択します
  2. サイズインスペクターに移動します
  3. [レイアウトの余白]セクションで、[スーパービューの余白を保持する]をオンにします(プラス記号をクリックしないでください)

    Checking preserve superview margins

  4. (そしてここにキーがあります)セル自体に対して同じを行います(もしそうならコンテンツビューの親)

    The cell and not the content view this time

  5. 次のように制約を設定します。Label.Leading = Superview.Leading Margin(定数0)

    Setting the constraint for the custom cell's label

これで、すべてのセルのラベルがデフォルトのセルと一致するようになりました!これはXcode 7以降で機能し、参照したスレッドに記載されている修正が含まれています。 IBとシミュレーターは、適切に配置されたラベルを表示するはずです。

Final result in the simulator

また、View Controllerのクラスなどで、プログラムでこれを行うこともできます。

cell.preservesSuperviewLayoutMargins = true
cell.contentView.preservesSuperviewLayoutMargins = true

または、起動時にUIAppearanceを1回呼び出すことでセットアップできます(ごめん、Swiftしか知りません)。

UITableViewCell.appearance().preservesSuperviewLayoutMargins = true
UITableViewCell.appearance().contentView.preservesSuperviewLayoutMargins = true

仕組みと理由

Ethan が親切に指摘したように、UIViewに関するApple自身のドキュメントはpreservesSuperviewLayoutMarginsを次のように説明しています。

このプロパティの値がtrueの場合、コンテンツをレイアウトするときにスーパービューのマージンも考慮されます。このマージンは、ビューのエッジとそのスーパービューの間の距離が対応するマージンよりも小さいレイアウトに影響します。たとえば、フレームがそのスーパービューの境界に正確に一致するコンテンツビューがあるとします。スーパービューのマージンのいずれかがコンテンツビューとそれ自体のマージンで表される領域内にある場合、UIKitはスーパービューのマージンを尊重するようにコンテンツビューのレイアウトを調整します。調整量は、コンテンツがスーパービューのマージン内に収まるようにするために必要な最小量です。

したがって、セルのコンテンツをTableViewのマージン(必要に応じてgreat祖父母)に合わせたい場合は、コンテンツの2つのアセンダントが必要です。 、コンテンツビュー、およびテーブルセル自体は、独自のスーパービューのマージンを保持します。

これがデフォルトの動作ではない理由は私を驚かせます。すべてをカスタマイズしたくないほとんどの開発者は、デフォルトでこの「継承」を期待しているように感じます。

148
Jonas Zaugg

私はあなたと同じ問題に出くわし、解決策を思いつきました。

まず、背景を少し説明します。iOS8以降、デフォルトのテーブルビューセルは、セルのlayoutMarginsを尊重して、さまざまな特性(別名、スクリーン、またはデバイス)に適応します。たとえば、すべてのiPhone(フォームシートに表示されるiPhone 6 Plusを除く)のレイアウトマージンは{8, 16, 8, 16}。 iPadでは{8, 20, 8, 20}。これで、4ピクセルの違いがあることがわかりました。これは、カスタムテーブルビューセルが考慮していない可能性が高いです。

テーブルビューセルサブクラスは、layoutMarginsが変更されたときに左マージンの制約を調整する必要があります。

関連するコードスニペットは次のとおりです。

 - (void)layoutMarginsDidChange
{
    [super layoutMarginsDidChange];

    self.leftLayoutMarginConstraint.constant = self.layoutMargins.left;
    self.rightLayoutMarginConstraint.constant = self.layoutMargins.right;
}

コードのレイアウトマージンに適応すると、タイトルラベルに適切なパディングを常に取得できます。

また、既にlayoutMarginsを尊重しているUITableViewCellサブクラスの1つを見ることができます。 https://github.com/bhr/BHRExtensions/blob/master/BHRExtensions/Utilities/BHRTitleAndValueTableCell.m

乾杯

6
tubtub

既存の回答を読んで、明らかなプログラムによる解決策を見つけられなかった後、私はさらに掘り下げて、この問題に直面している他の誰にとっても良い答えを得ました。

まず、preservesSuperviewLayoutMarginsをセルのビューまたはコンテンツビューに otheranswers として設定する必要はありません。デフォルト値はfalseですが、trueに変更しても目立った効果はありませんでした。

これを実際に機能させるための鍵は、layoutMarginsGuideUIViewプロパティです。この値を使用すると、サブビューのleadingAnchorをガイドのleadingAnchorに簡単に固定できます。これがコードでどのように見えるかを示します(そして Jonas's answer のように、IBが舞台裏で行っていることは非常によくあるかもしれません)。

UITableViewCellサブクラスでは、次のようなことをします。

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide
    let leading = margins.leadingAnchor
    subview1.leadingAnchor.constraintEqualToAnchor(leading).active = true
    subview2.leadingAnchor.constraintEqualToAnchor(leading).active = true

    super.updateConstraints()
}

Swift 4.1アップデート

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide
    let leading = margins.leadingAnchor
    subview1.leadingAnchor.constraint(equalTo: leading).isActive = true
    subview2.leadingAnchor.constraint(equalTo: leading).isActive = true

    super.updateConstraints()
}

それで全部です! iOS 9より前のiOSバージョン向けに開発している場合は、レイアウトアンカーを置き換えて、代わりにlayoutMarginsインセットを使用する必要があります。


注:よりクリーンな構文を希望する場合は、アンカーを少しきれいにするためのライブラリを作成しました。 SuperLayout と呼ばれ、Cocoapodsで利用できます。ソースファイルの先頭で、SuperLayoutをインポートします。

import SuperLayout

次に、レイアウトブロックで、~~≤≤、および≥≥を使用して制約を固定します。

override func updateConstraints() {
    let margins = contentView.layoutMarginsGuide

    subview1.leadingAnchor ~~ margins.leadingAnchor
    subview2.leadingAnchor ~~ margins.leadingAnchor

    super.updateConstraints()
}

ios 11+:マージン= contentView.directionalLayoutMarginsにしましょう...そのままLTRとRTLに適応する必要がある場合に備えて。私はほとんどの人がそれを必要とすると思います。

4
Dan Loewenherz

次の操作を行うことにより、標準セルに合わせたカスタムスタイルのセルを取得できました。

  1. [ドキュメントアウトライン]で、カスタムスタイルのセルの[コンテンツビュー]を選択します。
  2. サイズインスペクターに移動します。
  3. [レイアウトの余白]ドロップダウンで、[スーパービューの余白を保持]の横にある小さなプラス記号を押します。
  4. 「通常の幅x通常の高さ」というiPadサイズクラスを選択します。
  5. [スーパービューの余白を保持する]の横にあるチェックボックスをオンにします。
  6. フレームを更新して、自動レイアウトの警告を解決します。

これはXcode 7でうまくいきました。 Xcode 6でも機能することを期待しています。

1
Becky Hansmeyer

IPad Air、OS 10.1.1でテストするときにこの問題が発生しました。テーブルのヘッダーは本来あるべき位置よりもずっとインデントされており、横向きの場合はさらに悪化しました。しかし、OS 11までのiphoneでは問題ありませんでした。

驚くべき解決策は、テーブルが作成された直後の次のコード行でした(申し訳ありませんが、私はC#でしか作業していませんが、Obj-CとSwift同等のもの)は簡単に解決できます):

myTableView.SeparatorInset = myTableView.SeparatorInset;

その後、すべてが本来のようにインデントされました!

0
diyaddict