tableView:cellForIndexPath
の呼び出し中にテーブルビューセルを変更していますが、セルがスクロールオフされてから再びオンになるまで、変更は表示されません。 dequeue
を呼び出す代わりに、.xibファイルを変更したり、セルを割り当て/初期化したりできることはわかっていますが、何が起こっているのかを理解したいと思います。
変更には、丸みを帯びた下部コーナーを実現するためにセルをマスキングすることが含まれます。代わりに、すべてのコーナーを丸めるコーナー半径を設定した場合(つまり、cell.layer.cornerRadius = ...
)、問題は発生しません。
更新:
コードはプロプライエタリであり、非常に複雑であり、詳細がない限り、質問は妥当なものであると考えたため、最初はコードを投稿しませんでした。ただし、応答に照らして、ここに問題を示す縮小フラグメントがあります。唯一の変更は実際のセル識別子です。また、シナリオを簡素化するために説明/質問を更新しました。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TheCorrectCellIdentifier" forIndexPath:indexPath];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(4., 4.)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
return cell;
}
コードは正常に機能しています。ただし、cellForRowAtIndexPath:
でコーナー半径の問題が引き続き発生する場合は、willDisplayCell:
で試してください。私は両方の方法で試し、うまくいきました。
willDisplayCell:
テーブルビューが特定の行のセルを描画しようとしていることをデリゲートに通知します。
テーブルビューは、セルを使用して行を描画する直前にこのメッセージをデリゲートに送信します。これにより、デリゲートは、表示される前にセルオブジェクトをカスタマイズできます。このメソッドにより、デリゲートは、選択や背景色など、テーブルビューによって以前に設定された状態ベースのプロパティを上書きする機会が与えられます。デリゲートが戻った後、テーブルビューはアルファプロパティとフレームプロパティのみを設定し、行をスライドインまたはスライドアウトするときに行をアニメーション化する場合にのみ設定します。これが私のコードです。
私によると、Appleは、UITableViewを管理するためにUITableViewDataSource
とUITableViewDelegate
という名前の2つの異なるメソッドセット(プロトコル)を提供します。
UITableViewDataSource:データソースは、セルが詳細を入力するための実際のデータを提供します。
UITableViewDelegate:もう一方の端では、デリゲートはテーブルビューの視覚的なレイアウトに関する情報を提供し、ユーザーの操作を処理します。
したがって、結論として、データソースは主にテーブルのコンテンツ(データ)を扱い、デリゲートはテーブルビューコンポーネントの表示と相互作用を扱います。
TableViewの扱い方を見てみましょう。
私たちは皆、UITableViewDataSourceメソッドtableView:cellForRowAtIndexPath:
を非常に個人的に使用しています。このメソッドは、各インデックスパスの適切な詳細情報を使用して有効なUITableViewCellを作成または再利用します。このメソッドでセルレイアウトの構成とUIの変更を行う傾向があります。
tableView:cellForRowAtIndexPath:
が呼び出された後、システムはレイアウト構成を行い、セルが画面に表示される前にtableView:willDisplayCell:forRowAtIndexPath:
メソッドを呼び出します。
したがって、Tableviewセルのビュー構成にこのメソッドを使用できます(おそらく、Appleこのメソッドを提案してください!!!)。
そのため、私はtableView:cellForRowAtIndexPath:
を使用して、tableView:willDisplayCell:forRowAtIndexPath:
メソッドのセルと左セルのUI構成タスクを作成することを強く望んでいます。
willDisplayCell:
にマスクを適用します
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
}
これが私のcellForRowAtIndexPath:
です
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"Row : %ld ", (long)indexPath.section];
return cell;
}
出力:
TableViewをviewDidAppear
またはviewDidLayoutSubviews
メソッドでリロードすると機能します。
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
理由コードが機能しなかった理由:実際には、ビューのライフサイクルに従って、メソッドは次の方法で呼び出されます。init
、loadView
、viewDidLoad
、viewWillAppear
、viewWillLayoutSubviews
(複数回呼び出されます)、viewDidLayoutSubviews
(複数回呼び出されます)そして最後にviewDidAppear
。
これで、実際にはテーブルビューがメソッドviewDidLoad
に読み込まれますが、適用する自動レイアウト制約はそのメソッドの後に適用されます。そのため、期待した結果が得られていません。自動レイアウト制約が適用された後に再度ロードすると、機能します。それが、上記のコードが確実に機能する理由です。
注意すべきもう1つのポイント:ストーリーボードで、iphone 5サイズを使用してviewControllerを設計し、iphone 5でコードを実行した場合、コードをリロードせずに動作する必要がありますが、他のサイズのviewControllerで実行した場合はその後、それは動作しません。その背後にある理由は、viewDidLoad
メソッドが呼び出され、ビューのサイズがストーリーボードにあったためです。
ご不明な点がございましたら、お気軽にお問い合わせください
Approach - 1 reload cell in viewWillAppear with delay mathod
func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.10, execute: {
self.tableView.reloadData() })
}
Approach - 2 Draw in GCD
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TheCorrectCellIdentifier" forIndexPath:indexPath];
dispatch_async(dispatch_get_main_queue(), ^{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(4., 4.)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
});
return cell;
}
コードが正常に機能しているため、iOS9.2で問題を再現できません。
古いバージョンをサポートしている場合、直面している問題は、tableViewの幅に対応していないセルの初期幅に関連していると思います。セルが配置される前は、初期の幅はストーリーボードの幅(実際の画面の幅とは異なります)に基づいていました。
何が起こるかというと、shapeLayerマスクが間違った境界で始まるため、マスクが正しく表示されないuntilセルが再利用されます。その時点で、セルがレイアウトされているため、セルの幅が正しくなり、新しいマスクの境界が正しくなり、適切な効果が得られます。
cellForRowAtIndexPath
でレイアウトを実行することはできますが、初期セル幅の問題などのエッジケースを処理するためにもう少しコードが必要です。
マスクを適用する方がはるかに簡単ですセルが配置された後、たとえば 技術者が回答で提案 。
それが問題の説明です。
変更には、セルをマスキングして、丸みを帯びた下部コーナーを実現することが含まれます。代わりに、すべてのコーナーを丸めるコーナー半径を設定した場合(つまり、cell.layer.cornerRadius = ...)、問題は発生しません。
特定のフレームサイズのシェイプレイヤーを追加する必要がないため、問題が発生することはありません。レンダリングが発生すると、セルのレイヤーは適切なサイズになるため、丸みを帯びた角が正しく表示されます。
これをどのように処理する必要があったか
サポートする必要のあるiOSのバージョンはわかりません(この問題は最近のバージョンで修正されているため)が、スクロールオフするまでセルが最初に正しくレイアウトされなかった問題を回避する方法の1つです。 -画面、次に画面に戻ります。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
// Workaround for visible cells not laid out properly since their layout was
// based on a different (initial) width from the tableView.
CGRect rect = cell.frame;
rect.size.width = CGRectGetWidth(tableView.bounds);
cell.frame = rect;
... configure cell
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
setNeedsLayout
で逃げることができるかもしれません。 layoutIfNeeded
。私のアプリでは、他の理由で制約を更新する必要がありました。
TableView:cellForIndexPathの呼び出し中にテーブルビューセルを変更していますが、セルがスクロールオフされてから再びオンになるまで、変更は表示されません。
このメソッドは、セルをリサイクルする必要があるまで呼び出されません(つまり、画面外から表示されます)。すでにテーブルビューにあるセルは、すでに表示されているため、このメソッドは呼び出されません。デリゲートコールバックを混乱させたのではないかと思います(このコードは正常に機能するため)。これらのメソッドは、新しいセルがスクロールして表示されたときにのみトリガーされます(キャッシュされたセルを再構成する必要があります)。
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
を呼び出すと、セルの再描画をトリガーできます。または[tableview reloadData]
すべてのセルを更新します
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.reloadInputViews()
}