iOS 5の気の利いた行移動呼び出しを使用して、古いスタイルの削除と削除の代わりに、インサート。UITableView:using moveRowAtIndexPath:toIndexPath:andRoadRowsAtIndexPaths:withRowAnimation:一緒に壊れて表示されます。
変更にはリオーダとインプレースの両方の更新が含まれていて、両方をアニメーション化したいので、いくつかの行にはreloadRowsAtIndexPaths
が必要です。
しかし! UITableView
は、更新されたセルが移動のために位置を移動した場合に、移動の存在下で行の再ロードを処理するには間違っているように見えます。以前のdelete + insert呼び出しを使用すると、同等のはずの方法で正常に動作します。
ここにいくつかのコードがあります。私は冗談をお詫びしますが、コンパイルして実行します。肉はdoMoves:
メソッドにあります。以下の展覧会。
#define THISWORKS
@implementation ScrambledList // extends UITableViewController
{
NSMutableArray *model;
}
- (void)viewDidLoad
{
[super viewDidLoad];
model = [NSMutableArray arrayWithObjects:
@"zero",
@"one",
@"two",
@"three",
@"four",
nil];
[self.navigationItem setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:
#ifdef THISWORKS
@"\U0001F603"
#else
@"\U0001F4A9"
#endif
style:UIBarButtonItemStylePlain
target:self
action:@selector(doMoves:)]];
}
-(IBAction)doMoves:(id)sender
{
int fromrow = 4, torow = 0, changedrow = 2; // 2 = its "before" position, just like the docs say.
// some model changes happen...
[model replaceObjectAtIndex:changedrow
withObject:[[model objectAtIndex:changedrow] stringByAppendingString:@"\u2032"]];
id tmp = [model objectAtIndex:fromrow];
[model removeObjectAtIndex:fromrow];
[model insertObject:tmp atIndex:torow];
// then we tell the table view what they were
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:changedrow inSection:0]]
withRowAnimation:UITableViewRowAnimationRight]; // again, index for the "before" state; the tableview should figure out it really wants row 3 when the time comes
#ifdef THISWORKS
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:fromrow inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:torow inSection:0]]
withRowAnimation:UITableViewRowAnimationAutomatic];
#else // but this doesn't
[self.tableView moveRowAtIndexPath:[NSIndexPath indexPathForRow:fromrow inSection:0]
toIndexPath:[NSIndexPath indexPathForRow:torow inSection:0]];
#endif
[self.tableView endUpdates];
}
#pragma mark - Table view data source boilerplate, not very interesting
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return model.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@""];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@""];
[cell.textLabel setText:[[model objectAtIndex:indexPath.row] description]];
[cell.detailTextLabel setText:[NSString stringWithFormat:@"this cell was provided for row %d", indexPath.row]];
return cell;
}
小さなコード(小さな可変配列)を設定します。ボタンが押されると、リストの中央の要素を少し変更し、最後の要素を最初の要素に移動します。次に、テーブルビューを更新してこれらの変更を反映します。中央の行をリロードし、最後の行を削除して新しい行ゼロを挿入します。
これは機能します。実際には、cellForRowAtIndexPath
にログを追加すると、2行の再読み込みを要求されますが、実際に更新を行う時間があると、tableviewは正しく3行目を要求します。フツァ!
ここで、moveRowAtIndexPathを使用する代わりに#ifdefをコメントアウトしてください。
今テーブルビューが行2を除去し、(間違った!)新鮮行を求め、最終行2位置(また、間違った!)の中に挿入します。正味の結果は、行1が2つのスロットの代わりに2つのスロットの代わりにスクロールし、画面をスクロールして強制的にリロードすることで、モデルとの同期が外れていることを示します。 moveRowAtIndexPath
が、別の順序でテーブルビューのプライベートモデルを変更した場合、リロードやモデルフェッチで「古い」インデックスパスの代わりに「新しい」インデックスを使用する必要があることは理解できました。 2番目の「後」の写真では、3番目と4番目の行が逆の順序になっています。どちらのセルを読み込んでも問題ありません。
私の語彙は、カラフルなのろいのアップルが成長してきました。私は代わりに自分自身を呪っているべきですか?行は、同じ更新ブロック内の行再ロードと単純に互換性がありません(疑わしいのは、挿入と削除だけです)?バグレポートを提出する前に誰かが私を啓発することはできますか?
非常に興味深い! –
あなたはこれに対する解決策を見つけましたか?私はまったく同じ問題に取り組んでいます。 –
私は不十分な情報で閉じられたバグレポートを提出し、ios6の下でテストする要求を出しましたが(問題は解決していないかもしれませんが、UICollectionViewにも同様の問題があります) 2つの別々のbegin/endUpdatesブロック、1つはリロード、もう1つはmove/inserts/deletesのためのものです。またはreloadRowsAtIndexPathsを完全にスキップして、適切なセルを手動で検索/再投入します。 – rgeorge