web-dev-qa-db-ja.com

すばやくスワイプして戻ったときにUITableViewCellの選択が解除されない

3つのアプリをiOS 7に更新しましたが、3つすべてで、コードを共有していないにもかかわらず、ユーザーがスワイプして(戻るボタンをタップするのではなく)Navigation Controllerに戻ると問題が発生します、セルは選択状態のままになります。

3つのアプリでは、1つはプログラムで作成されたカスタムセルを使用し、もう1つはストーリーボードで作成されたカスタムセルを使用し、3つ目はストーリーボードでもUITableViewの非常に基本的なサブクラスのデフォルトセルを使用します。 3つすべてのケースで、セルは選択解除されません。ユーザーがゆっくりとスワイプするか、戻るボタンを押すと、通常どおり選択が解除されます。

これは私のiOS 7アプリでのみ発生し、Apple自身のアプリとiOS 7用にアップグレードされたサードパーティアプリはすべて正常に動作しているようです(ただし、セルの選択解除の速さにはわずかな違いはありますが)。

私が間違っていることはあるに違いありませんが、何がわからないのですか?

59
Robert

私は今、同じ問題に取り組んでいます。 ICatalog -sample from Appleは汚い解決策をもたらすようです。

本当に嬉しくはありません。前述のように、[self.tableView deselectRowAtIndexPath:tableSelection animated:NO];を使用して、現在選択されている行の選択を解除します。

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // this UIViewController is about to re-appear, make sure we remove the current selection in our table view
    NSIndexPath *tableSelection = [self.tableView indexPathForSelectedRow];
    [self.tableView deselectRowAtIndexPath:tableSelection animated:NO];

    // some over view controller could have changed our nav bar tint color, so reset it here
    self.navigationController.navigationBar.tintColor = [UIColor darkGrayColor];
}

サンプルコードが iOS 7iOS 8iOS 9 iOS 10対応


私を本当に混乱させているのは、 ITableViewController Class Reference

テーブルビューが初めて読み込まれたときに表示されるとき、Table View Controllerはテーブルビューのデータを再読み込みします。 また、テーブルビューが表示されるたびに、選択をクリアします(リクエストに応じて、アニメーションの有無にかかわらず)。UITableViewControllerクラスこれをスーパークラスメソッドviewWillAppear:で実装します。 clearsSelectionOnViewWillAppearプロパティの値を変更することにより、この動作を無効にできます。

これはまさに私が期待する動作です...しかし、動作しないようです。あなたにも私にも。 「ダーティ」ソリューションを実際に使用して、自分で実行する必要があります。

36
Fabio Poloni

これは私にとって最もうまくいきました:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.clearsSelectionOnViewWillAppear = NO;
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:animated];
}

ゆっくりと後ろにスワイプしている間、私ははるかに優れた選択解除フェーディングさえも得ました。

54
emreberge

Fabioの答えはうまく機能しますが、ユーザーが少しスワイプしてから気が変わった場合、正しい外観を与えません。そのケースを正しく取得するには、選択したインデックスパスを保存し、必要に応じてリセットする必要があります。

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.savedSelectedIndexPath = nil;
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    if (self.savedSelectedIndexPath) {
        [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
    }
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.savedSelectedIndexPath = self.tableView.indexPathForSelectedRow;

    if (self.savedSelectedIndexPath) {
        [self.tableView deselectRowAtIndexPath:self.savedSelectedIndexPath animated:YES];
    }
}

UITableViewControllerを使用している場合は、組み込みのクリアを無効にしてください:

self.clearsSelectionOnViewWillAppear = NO;

savedSelectedIndexPathのプロパティを追加します。

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;

いくつかの異なるクラスでこれを行う必要がある場合は、たとえばこの要点でやったように、ヘルパーで分割するのが理にかなっています: https://Gist.github.com/rhult/46ee6c4e8a862a8e66d4

19
Rhult

このソリューションは、行の選択解除を遷移コーディネーターとともにアニメーション化し(ユーザー駆動VC dismiss))、ユーザーが遷移をキャンセルした場合に選択を再適用します。 SwiftのCaleb Davenport 。iOS 9でのみテスト済み。ユーザー駆動(スワイプ)トランジションと古いスタイルの「戻る」ボタンタップの両方で動作することがテスト済み。

UITableViewControllerサブクラス内:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // Workaround. clearsSelectionOnViewWillAppear is unreliable for user-driven (swipe) VC dismiss
    NSIndexPath *indexPath = self.tableView.indexPathForSelectedRow;
    if (indexPath && self.transitionCoordinator) {
        [self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            [self.tableView deselectRowAtIndexPath:indexPath animated:animated];
        } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
            if ([context isCancelled]) {
                [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
            }
        }];
    }
}
9
nevan king

今日自分でこれに遭遇した後、これは明らかにUITableViewでかなりよく知られている問題であることがわかりました。インタラクティブなナビゲーション遷移のサポートはわずかに壊れています。カストロの背後にいる人々は、これに対する優れた分析と解決策を投稿しています: http://blog.supertop.co/post/80781694515/viewmightappear

私はキャンセルされたトランジションも考慮する彼らのソリューションを使用することにしました:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    NSIndexPath *selectedRowIndexPath = [self.tableView indexPathForSelectedRow];
    if (selectedRowIndexPath) {
        [self.tableView deselectRowAtIndexPath:selectedRowIndexPath animated:YES];
        [[self transitionCoordinator] notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> context) {
            if ([context isCancelled]) {
                [self.tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
            }
        }];
    }
}
7
CodeStage

設定しようとすることができます

self.clearsSelectionOnViewWillAppear = YES;

uITableViewControllerまたは

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:NO];

viewWillAppearで、おそらく[super viewWillAppear:animated]を呼び出す前。 UItableViewがUITableViewController内でnotの場合、セルを手動で選択解除する必要があります。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES]; 
}
5
falsecrypt

シンプルSwift 3/4 Answer:

override func viewWillAppear(_ animated: Bool) {
    if tableView.indexPathForSelectedRow != nil {
        self.tableView.deselectRow(at: tableView.indexPathForSelectedRow! as IndexPath, animated: true)
    }
}
2
SteffenK

私は使っています

[tableView deselectRowAtIndexPath:indexPath animated:YES];

メソッドの最後に

(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

このような:

(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    //doing something according to selected cell...

    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}
2
Evgeny

デフォルトの動作を正常に機能させるだけの、この問題の非常に簡単な解決策を見つけました。結果の視覚効果がわずかに異なるため、deselectRowAtIndexPathを含むソリューションに満足しませんでした。

この奇妙な動作を防ぐために必要なことは、ビューが表示されたときにテーブルをリロードすることだけです。

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    [self.tableView reloadData];
}
1
quentez

Codestage answer 、in Swift 3. in notifyWhenInteractionEndsは非推奨です。

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)

    if let indexPath = self.tableView.indexPathForSelectedRow {
        self.tableView.deselectRow(at: indexPath, animated: true)
        self.transitionCoordinator?.notifyWhenInteractionChanges { (context) in
            if context.isCancelled {
                self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
            }
        }
    }
}
1
Mike

おそらく、スーパービューのviewWillAppearメソッド([super viewWillAppear:animated];)を呼び出していません。これを行い、UITableViewControllerのパラメーターclearsSelectionOnViewWillAppearがYESの場合、viewWillAppearでセルの選択が解除されます。

1
Bessi

使用する

_[tableView deselectRowAtIndexPath:indexPath animated:YES];_

コードイン

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath method

1
Pradhyuman sinh

Swiftの場合

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    guard let indexPath = tableView.indexPathForSelectedRow else{
        return
    }
    tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
0
Sofeda

Rhultのソリューション iOS 9.2で完全に動作します。これは、Swiftの実装です。

IndexPathを保存するには、MasterViewControllerで変数を宣言します。

var savedSelectedIndexPath: NSIndexPath?

次に、明確にするためにコードを拡張機能に配置できます。

extension MasterViewController {
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        self.savedSelectedIndexPath = nil
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        if let indexPath = self.savedSelectedIndexPath {
            self.tableView.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
        }
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.savedSelectedIndexPath = tableView.indexPathForSelectedRow
        if let indexPath = self.savedSelectedIndexPath {
            self.tableView.deselectRowAtIndexPath(indexPath, animated: true)
        }
    }
}
0
Kymer

Rhult のコードに基づいて、いくつかの変更を加えました。

この実装により、ユーザーはスワイプバックをキャンセルしても、今後のスワイプバック選択解除アニメーションのために選択されたままになります。

@property(strong, nonatomic) NSIndexPath *savedSelectedIndexPath;


- (void)viewDidLoad {
   [super viewDidLoad];
   self.clearsSelectionOnViewWillAppear = NO;
}

-(void) viewDidAppear:(BOOL)animated {
   [super viewDidAppear:animated];
   self.savedSelectedIndexPath = nil;
}

-(void) viewWillDisappear:(BOOL)animated {
   [super viewWillDisappear:animated];
   if (self.savedSelectedIndexPath && ![self.tableView indexPathForSelectedRow]) {
       [self.tableView selectRowAtIndexPath:self.savedSelectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
   } else {
      self.savedSelectedIndexPath = [self.tableView indexPathForSelectedRow];
   }
}
0
Simon

コードステージは、最も見栄えの良い答えを提供してくれました なので、Swift 2。

override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(true)

        let selectedRowIndexPath = self.tableView.indexPathForSelectedRow
        if ((selectedRowIndexPath) != nil) {
            self.tableView.deselectRowAtIndexPath(selectedRowIndexPath!, animated: true)
            self.transitionCoordinator()?.notifyWhenInteractionEndsUsingBlock({ context in
                if (context.isCancelled()) {
                    self.tableView.selectRowAtIndexPath(selectedRowIndexPath, animated: false, scrollPosition: UITableViewScrollPosition.None)
                }
            })
        }
    }
0
AppreciateIt