122

アイテムのリストを含むUITableViewがあります。アイテムを選択すると、viewControllerがプッシュされ、次に次の処理が実行されます。メソッドviewDidLoadからサブビューのonで必要なデータのURLRequestを起動します-drawRectがオーバーライドされたUIViewサブクラス。クラウドからデータが到着すると、ビュー階層の構築を開始します。問題のサブクラスにデータが渡され、そのdrawRectメソッドにレンダリングに必要なすべてのものが含まれるようになりました。

だが。

drawRectを明示的に呼び出さないため(Cocoa-Touchはそれを処理します)、Cocoa-Touchに本当にこのUIViewサブクラスをレンダリングしたいことを通知する方法がありません。いつ?今がいいでしょう!

[myViewsetNeedsDisplay]を試しました。これはちょっとうまくいくことがあります。非常にむらがある。

私は何時間もこれと格闘してきました。UIViewの再レンダリングを強制するための堅実で保証されたアプローチを私に提供してくれる人がいますか?

ビューにデータをフィードするコードのスニペットは次のとおりです。

// Create the subview
self.chromosomeBlockView = [[[ChromosomeBlockView alloc] initWithFrame:frame] autorelease];

// Set some properties
self.chromosomeBlockView.sequenceString     = self.sequenceString;
self.chromosomeBlockView.nucleotideBases    = self.nucleotideLettersDictionary;

// Insert the view in the view hierarchy
[self.containerView          addSubview:self.chromosomeBlockView];
[self.containerView bringSubviewToFront:self.chromosomeBlockView];

// A vain attempt to convince Cocoa-Touch that this view is worthy of being displayed ;-)
[self.chromosomeBlockView setNeedsDisplay];

乾杯、ダグ

4

6 に答える 6

196

UIViewを強制的に再レン​​ダリングするための保証された堅実な方法は[myView setNeedsDisplay]. これに問題がある場合は、次のいずれかの問題が発生している可能性があります。

  • 実際にデータを取得する前に呼び出しているか、-drawRect:何かを過剰にキャッシュしています。

  • このメソッドを呼び出した時点で、ビューが描画されることを期待しています。Cocoa 描画システムを使用して「今、この瞬間に描画する」ことを要求する方法は意図的にありません。これにより、ビュー合成システム全体が混乱し、パフォーマンスが低下し、あらゆる種類のアーティファクトが作成される可能性があります。「これは次の描画サイクルで描画する必要がある」と言うしかありません。

必要なものが「何らかのロジック、描画、その他のロジック」である場合、「その他のロジック」を別のメソッドに-performSelector:withObject:afterDelay:配置し、0 の遅延で使用して呼び出す必要があります。これにより、「その他のロジック」が後に配置されます。次の描画サイクル。この種のコードの例と、それが必要になる可能性があるケースについては、この質問を参照してください(ただし、コードが複雑になるため、可能であれば他の解決策を探すのが通常は最善です)。

物事が描かれていないと思われる場合は、ブレークポイントを入れて、-drawRect:いつ呼び出されるかを確認してください。を呼び出しているが、次のイベント ループで呼び出されない場合は-setNeedsDisplay-drawRect:ビュー階層を掘り下げて、どこかの裏をかこうとしていないことを確認してください。私の経験上、絵が下手になる一番の原因は頭が良すぎることです。システムをだまして自分のやりたいことをさせる方法を自分が一番よく知っていると思っていても、たいていは自分が望んでいないことをやってしまうことになります。

于 2009-10-01T13:02:25.600 に答える
53

setNeedsDisplay と drawRect: (5 秒) を呼び出す間に大きな遅延が発生するという問題がありました。メインスレッドとは別のスレッドで setNeedsDisplay を呼び出したことが判明しました。この呼び出しをメインスレッドに移動した後、遅延はなくなりました。

これが役立つことを願っています。

于 2010-11-26T10:16:35.753 に答える
19

ビューを強制的に同期的に描画する (呼び出し元のコードに戻る前に)返金が保証された鉄筋コンクリートの確実な方法は、 とサブクラスCALayerとの相互作用を構成することです。UIView

UIView サブクラスでdisplayNow()、レイヤーに「<em>表示コースを設定」してから「<em>そのようにする」ように指示するメソッドを作成します。

迅速

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
public func displayNow()
{
    let layer = self.layer
    layer.setNeedsDisplay()
    layer.displayIfNeeded()
}

Objective-C

/// Redraws the view's contents immediately.
/// Serves the same purpose as the display method in GLKView.
- (void)displayNow
{
    CALayer *layer = self.layer;
    [layer setNeedsDisplay];
    [layer displayIfNeeded];
}

draw(_: CALayer, in: CGContext)また、プライベート/内部描画メソッドを呼び出すメソッドを実装します(すべてUIViewが a であるため、これは機能しますCALayerDelegate)

迅速

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
override func draw(_ layer: CALayer, in context: CGContext)
{
    UIGraphicsPushContext(context)
    internalDraw(self.bounds)
    UIGraphicsPopContext()
}

Objective-C

/// Called by our CALayer when it wants us to draw
///     (in compliance with the CALayerDelegate protocol).
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
    UIGraphicsPushContext(context);
    [self internalDrawWithRect:self.bounds];
    UIGraphicsPopContext();
}

internalDraw(_: CGRect)そして、fail-safe とともにカスタム メソッドを作成しますdraw(_: CGRect)

迅速

/// Internal drawing method; naming's up to you.
func internalDraw(_ rect: CGRect)
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
override func draw(_ rect: CGRect) {
    internalDraw(rect)
}

Objective-C

/// Internal drawing method; naming's up to you.
- (void)internalDrawWithRect:(CGRect)rect
{
    // @FILLIN: Custom drawing code goes here.
    //  (Use `UIGraphicsGetCurrentContext()` where necessary.)
}

/// For compatibility, if something besides our display method asks for draw.
- (void)drawRect:(CGRect)rect {
    [self internalDrawWithRect:rect];
}

そしてmyView.displayNow()、描画するために本当に本当に必要なときはいつでも呼び出すだけです(コールバックなどからCADisplayLink。私たちのdisplayNow()メソッドは to に通知CALayerdisplayIfNeeded()、これは同期的に私たちにコールバックしdraw(_:,in:)、 で描画を行い、internalDraw(_:)先に進む前にコンテキストに描画されたものでビジュアルを更新します。


このアプローチは、上記の @RobNapier のものと似ていますが、displayIfNeeded()に加えて呼び出すという利点がありsetNeedsDisplay()、これにより同期が行われます。

これが可能なCALayerのは、 が よりも多くの描画機能を公開しているためです。UIViewレイヤーはビューよりも下位レベルであり、レイアウト内で高度に構成可能な描画を目的として明示的に設計されており、(Cocoa の多くのものと同様に) 柔軟に使用できるように設計されています (親クラスとして、または委任者として、または他の描画システムへのブリッジとして、または単独で)。プロトコルを適切に使用することで、CALayerDelegateこれらすべてが可能になります。

CALayerの設定可能性に関する詳細は、コア アニメーション プログラミング ガイド の「レイヤ オブジェクトの設定」セクションを参照してください。

于 2013-04-06T01:29:28.210 に答える
5

私は同じ問題を抱えていました.SOまたはGoogleのすべてのソリューションは私にとってはうまくいきませんでした. 通常は機能しますsetNeedsDisplayが、機能しない場合は...すべての可能なスレッドやものから可能な限りすべての方法でビュー
を呼び出してみましたがsetNeedsDisplay、まだ成功していません。ロブが言ったように、私たちはそれを知っています

「これは次の描画サイクルで描画する必要があります。」

しかし、なぜか今回は描かれませんでした。そして、私が見つけた唯一の解決策は、しばらくしてから手動で呼び出して、次のようにドローをブロックするものをすべて通過させることです。

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 
                                        (int64_t)(0.005 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
    [viewToRefresh setNeedsDisplay];
});

ビューを頻繁に再描画する必要がない場合、これは良い解決策です。それ以外の場合、何らかの移動 (アクション) を行っている場合は、通常、 を呼び出すだけで問題はありませんsetNeedsDisplay

私と同じように、そこで迷子になっている人の助けになれば幸いです。

于 2013-02-22T15:21:02.427 に答える
0

これが大きな変更になるか、プロジェクトに適さない可能性があることは承知していますが、データを取得するまでプッシュを実行しないことを検討しましたか? そうすれば、ビューを 1 回描画するだけで済み、ユーザー エクスペリエンスも向上します。プッシュは既にロードされています。

これを行う方法は、UITableView didSelectRowAtIndexPath非同期にデータを要求することです。応答を受け取ったら、手動でセグエを実行し、データを で viewController に渡しますprepareForSegue。その間、簡単な読み込みインジケータチェックのために、いくつかのアクティビティインジケータを表示したい場合があります https://github.com/jdg/MBProgressHUD

于 2013-06-05T15:40:04.327 に答える