15

UITableView 内で Auto Layout クラスと size クラスを使用しており、セルの内容に基づいてセルサイズが自動的に調整されます。このために、セルの種類ごとに、そのセルのオフスクリーン インスタンスを保持し、それを使用しsystemLayoutSizeFittingSize正しい行の高さを決定する方法を使用しています

サイズクラスを使い始めるまで、これはうまくいきました。具体的には、Regular Width レイアウトのテキストの余白制約にさまざまな定数を定義したため、iPad ではテキストの周囲に余白が多くなります。これにより、次の結果が得られます。

前後

新しい一連の制約が適用されているように見えますが (余白が増えています)、行の高さの計算では、サイズ クラス固有の制約が適用されなかったセルの場合と同じ値が返されます。オフスクリーン セルのレイアウト プロセスの一部で、ウィンドウのサイズ クラスが考慮されていません

これはおそらく、オフスクリーン ビューにスーパービューやウィンドウなく、呼び出しが発生した時点で参照するサイズ クラスの特性がないためだと考えましたsystemLayoutSizeFittingSize(ただし、調整された制約を使用しているように見えますが、余白)。オフスクリーン サイズ変更セルを作成後に UIWindow のサブビューとして追加することで、これを回避します。これにより、目的の結果が得られます。

修繕

これが私がコードでやっていることです:

func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    let contentItem = content[indexPath.item]

    if let contentType = contentItem["type"] {
        // Get or create the cached layout cell for this cell type.
        if layoutCellCache.indexForKey(contentType) == nil {
            if let cellIdentifier = CellIdentifiers[contentType] {
                if var cachedLayoutCell = dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell {                        
                    UIApplication.sharedApplication().keyWindow?.addSubview(cachedLayoutCell)
                    cachedLayoutCell.hidden = true
                    layoutCellCache[contentType] = cachedLayoutCell
                }
            }
        }

        if let cachedLayoutCell = layoutCellCache[contentType] {
            // Configure the layout cell with the requested cell's content.
            configureCell(cachedLayoutCell, withContentItem: contentItem)

            // Perform layout on the cached cell and determine best fitting content height.
            cachedLayoutCell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), 0);
            cachedLayoutCell.setNeedsLayout()
            cachedLayoutCell.layoutIfNeeded()

            return cachedLayoutCell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
        }
    }

    fatalError("not enough information to determine cell height for item \(indexPath.item).")
    return 0
}

描画されるはずのないビューをウィンドウに追加することは、私にはハックのように思えます。現在ビュー階層にない場合でも、UIViews にウィンドウのサイズ クラスを完全に採用させる方法はありますか? それとも、私が見逃しているものがありますか?ありがとう。

4

2 に答える 2

9

2015 年 12 月の更新:

Apple は現在、オーバーライドを思いとどまらせています-traitCollection。他の回避策の使用を検討してください。ドキュメントから:

重要

traitCollectionプロパティを直接使用します。オーバーライドしないでください。カスタム実装を提供しないでください。


元の回答:

既存の答えは素晴らしいです。問題は次のとおりであると説明しました。

提案された回避策は、テーブル ビューにセルを一時的に追加することです。ただし、テーブル ビューの 、ビュー コントローラのビュー、さらにはビュー コントローラ自体がまだ有効になっていない-viewDidLoad、たとえば にいる場合、これは機能しません。traitCollection

traitCollectionここでは、セルをオーバーライドする別の回避策を提案します。そうするために:

  1. UITableViewCellセルの のカスタム サブクラスを作成します(既に行っている可能性があります)。

  2. - (UITraitCollection *)traitCollectionカスタム サブクラスで、プロパティのゲッターをオーバーライドするメソッドを追加しtraitCollectionます。これで、好きな有効なものを返すことができUITraitCollectionます。実装例は次のとおりです。

    // Override getter of traitCollection property
    // https://stackoverflow.com/a/28514006/1402846
    - (UITraitCollection *)traitCollection
    {
        // Return original value if valid.
        UITraitCollection* originalTraitCollection = [super traitCollection];
        if(originalTraitCollection && originalTraitCollection.userInterfaceIdiom != UIUserInterfaceIdiomUnspecified)
        {
            return originalTraitCollection;
        }
    
        // Return trait collection from UIScreen.
        return [UIScreen mainScreen].traitCollection;
    }
    

    または、 create メソッドのいずれかUITraitCollectionを使用して適切な create を返すこともできます。

    + (UITraitCollection *)traitCollectionWithDisplayScale:(CGFloat)scale
    + (UITraitCollection *)traitCollectionWithTraitsFromCollections:(NSArray *)traitCollections
    + (UITraitCollection *)traitCollectionWithUserInterfaceIdiom:(UIUserInterfaceIdiom)idiom
    + (UITraitCollection *)traitCollectionWithHorizontalSizeClass:(UIUserInterfaceSizeClass)horizontalSizeClass
    + (UITraitCollection *)traitCollectionWithVerticalSizeClass:(UIUserInterfaceSizeClass)verticalSizeClass
    

    または、次のようにしてより柔軟にすることもできます。

    // Override getter of traitCollection property
    // https://stackoverflow.com/a/28514006/1402846
    - (UITraitCollection *)traitCollection
    {
        // Return overridingTraitCollection if not nil,
        // or [super traitCollection] otherwise.
        // overridingTraitCollection is a writable property
        return self.overridingTraitCollection ?: [super traitCollection];
    }
    

プロパティは iOS 8 以降で定義されているため、この回避策は iOS 7 と互換性がありtraitCollectionます。そのため、iOS 7 では誰も getter を呼び出さず、オーバーライド メソッドを呼び出すこともありません。

于 2015-02-14T08:52:56.553 に答える
5

サイズクラスを使用してiPadとiPhoneなどでフォントサイズを簡単に変更できるようにした後、これに何日も費やしました。

問題の根本は、dequeueReusableCellWithIdentifier:取得元のスーパービューを持たないセルを返すことのようUITraitCollectionです。dequeueReusableCellWithIdentifier:forIndexPath:一方、スーパービューが であるセルを返しますUITableViewWrapperView

サイズクラスをサポートするためにこのメソッドを拡張していないため、Apple にバグレポートを提出しました。iOS7 でサイズ クラスを処理する方法は文書化されていないようです。UITableViewセルを要求するメッセージを送信すると、メッセージの送信先のテーブルのサイズ クラスを反映したものを返す必要があります。の場合ですdequeueReusableCellWithIdentifier:forIndexPath:

また、新しい自動レイアウト メカニズムを使用しようとするとviewDidAppear:、新しいメカニズムを適切に機能させるために、テーブルを再読み込みする必要があることがよくあります。これがないと、iOS7 アプローチを使用した場合と同じ問題が発生します。

私が知る限り、同じコードから iOS8 の自動レイアウトと iOS7 の古いメカニズムを使用することはできないようです。

今のところ、プロトタイプ セルをテーブルのサブビューとして追加し、サイズの計算を行ってから削除することで、問題を回避する必要がありました。

UITableViewCell *prototype=nil;
CGFloat prototypeHeight=0.0;

prototype=[self.tableView dequeueReusableCellWithIdentifier:@"SideMenuCellIdentifier"];

// Check for when the prototype cell has no parent view from 
// which to inherit size class related constraints.
BOOL added=FALSE;
if (prototype.superview == nil){
   [self.tableView addSubview:prototype];
   added=TRUE;
}

<snip ... Setup prototype cell>

[prototype setNeedsLayout];
[prototype layoutIfNeeded];
CGSize size = [prototype.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
prototypeHeight=size.height+1; // Add one for separator

// Remove the cell if added. Leaves it when in iOS7.
if (added){
  [prototype removeFromSuperview];
}

サイズ クラス関連の設定UITraitCollectionは、 の読み取り専用プロパティである を介して制御されているようですUIViewController。iOS7 の下位互換性のために、これはビルド システムによっていくつかの制限付きの回避策として処理されるようです。traitCollectionつまり、iOS7 ではプロパティにアクセスできませんが、iOS8 ではアクセスできます。

ストーリーボードからのビュー コントローラーとの密接な結合と、下位互換性のしくみを考えると、プロトタイプ セルは、Xcode で定義したビュー コントローラーの階層内にある必要があるように見えます。

これに関する議論がここにあります:
Xcode 6 アダプティブ UI は、iOS 7 および iOS 6 と下位互換性を持つことができますか?

于 2015-01-06T12:32:10.357 に答える