reloadData 后保存 UITableView 中选定的行

发布于 2024-08-13 06:30:38 字数 462 浏览 2 评论 0原文

我在 iPhone 中编写自定义 Jabber 客户端。

我使用 xmppframework 作为引擎。

我有 UITableViewController 和 NSMutableArray 作为代表联系人列表。

当我收到(或有人更改其内容)名册(又名联系人列表)时,我想更改 UITableView 项目(添加/删除/修改)。因此,如果用户在用户更新列表时使用 listView

[items addObject:newItem];
[self.tableView reloadData];

丢失当前选择项。

所以,我的问题是如何在重新加载数据后保存(如果可能,我的意思是如果给定的选定项目未删除)当前选择项目?

谢谢。

I write custom jabber client in iphone.

I use xmppframework as engine.

And I have UITableViewController with NSMutableArray for repesent contact list.

When i receive(or somebody change it contents) roster (aka contact list) i wanna change UITableView items (add/remove/modify). So if User work with listView at time when list updates by

[items addObject:newItem];
[self.tableView reloadData];

user lost current selection item.

So, my question is howto save (if possible, i mean if given selected item not removed) current select item after reloadData?

Thx.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

甜心 2024-08-20 06:30:39

这对我有用:

NSIndexPath *indexPath = [table indexPathForSelectedRow];
[table reloadData];
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self.table selectRowAtIndexPath:indexPath animated:YES
                              scrollPosition:UITableViewScrollPositionNone];
});

技巧是让 selectRowAtIndexpath 稍后运行一点。上面的代码是一个 Xcode 模板,您可以在开始编写 dispatch_after 时选择。

This works for me:

NSIndexPath *indexPath = [table indexPathForSelectedRow];
[table reloadData];
double delayInSeconds = 0.01;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [self.table selectRowAtIndexPath:indexPath animated:YES
                              scrollPosition:UITableViewScrollPositionNone];
});

The trick is to make selectRowAtIndexpath run later a bit. The code above is an Xcode template you can select when you start writing dispatch_after.

冬天的雪花 2024-08-20 06:30:39

如果您想要更新表并保留选择,这是一种解决方案:

    NSIndexPath* pathToSelect = [self.tableView indexPathForSelectedRow];
    if (pathToSelect && newRows) {
        int row = pathToSelect.row;
        for (NSIndexPath* path in newRows) {
            if (path.section == pathToSelect.section && path.row <= pathToSelect.row) {
                row++;
            }
        }
        pathToSelect = [NSIndexPath indexPathForRow:row inSection:pathToSelect.section];
    }

    [self.tableView beginUpdates];
    if (reloadRows) [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationFade];
    if (newSections) [self.tableView insertSections:newSections withRowAnimation:UITableViewRowAnimationFade];
    if (newRows) [self.tableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];

    if (pathToSelect) {
        [self.tableView selectRowAtIndexPath:pathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
    }

This is a solution for the case if you want to do table updates and keep the selection:

    NSIndexPath* pathToSelect = [self.tableView indexPathForSelectedRow];
    if (pathToSelect && newRows) {
        int row = pathToSelect.row;
        for (NSIndexPath* path in newRows) {
            if (path.section == pathToSelect.section && path.row <= pathToSelect.row) {
                row++;
            }
        }
        pathToSelect = [NSIndexPath indexPathForRow:row inSection:pathToSelect.section];
    }

    [self.tableView beginUpdates];
    if (reloadRows) [self.tableView reloadRowsAtIndexPaths:reloadRows withRowAnimation:UITableViewRowAnimationFade];
    if (newSections) [self.tableView insertSections:newSections withRowAnimation:UITableViewRowAnimationFade];
    if (newRows) [self.tableView insertRowsAtIndexPaths:newRows withRowAnimation:UITableViewRowAnimationFade];
    [self.tableView endUpdates];

    if (pathToSelect) {
        [self.tableView selectRowAtIndexPath:pathToSelect animated:NO scrollPosition:UITableViewScrollPositionNone];
    }
静待花开 2024-08-20 06:30:39

编辑:

经过测试,
它并不总是有效!,

原因是索引可能会改变!

所以正确的方法是
创建新变量

var currentIndex : IndexPath?

并在 didSelectRow 上

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        currentIndex = indexPath
}

然后将函数更改为

 func reloadTableView() {
        let indexPaths = tableview.indexPathsForSelectedRows
        if let currentIndex = self.currentIndex {
        self.tableview.reloadRows(at: [currentIndex], with: .fade)
        for path in indexPaths ?? [] {
            tableview.selectRow(at: path, animated: false, scrollPosition: .none)
            }}
    }

==========

@marmor 答案有效,

的 Swift 版本

这是他的代码Swift 4.2.3

func reloadTableView() {
        let indexPaths = tableView.indexPathsForSelectedRows
        tableView.reloadData()
        for path in indexPaths ?? [] {
            tableView.selectRow(at: path, animated: false, scrollPosition: .none)
        }
    }

Edit:

After testing,
it doesn't always work!,

the reason is the index may change !

so the correct way is
make new variable

var currentIndex : IndexPath?

and on didSelectRow

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        currentIndex = indexPath
}

then change the function to

 func reloadTableView() {
        let indexPaths = tableview.indexPathsForSelectedRows
        if let currentIndex = self.currentIndex {
        self.tableview.reloadRows(at: [currentIndex], with: .fade)
        for path in indexPaths ?? [] {
            tableview.selectRow(at: path, animated: false, scrollPosition: .none)
            }}
    }

==========

@marmor Answer worked

this is a Swift version of his code

Swift 4.2.3

func reloadTableView() {
        let indexPaths = tableView.indexPathsForSelectedRows
        tableView.reloadData()
        for path in indexPaths ?? [] {
            tableView.selectRow(at: path, animated: false, scrollPosition: .none)
        }
    }
虐人心 2024-08-20 06:30:39

很多这些答案基本上都是使用黑客来重新选择之前突出显示的行,方法是从表视图中找出之前选择的行,这似乎是一个不好的方法,因为通常当你的表视图重新加载时,填充表视图的数据发生变化。

因此,请根据您的数据重新选择行

let data = ["some", "array", "of", "data"]
let selected = "of" // this should be populated from didSelectRowAt indexPath:

tableView.reloadData()

if let index = data.firstIndex(of: selected) {
  tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none) // the "of" row should now be highlighted, regardless if the data changed order
}

A lot of these answers are basically using hacks to re-select the row that was previously highlighted by finding out what the previously selected row was from the table view which seems like a bad way to go about it, because usually when your tableview reloads, there is a change in the data that is populating the tableview.

So rather base you re-selection of the row based on your data

let data = ["some", "array", "of", "data"]
let selected = "of" // this should be populated from didSelectRowAt indexPath:

tableView.reloadData()

if let index = data.firstIndex(of: selected) {
  tableView.selectRow(at: IndexPath(row: index, section: 0), animated: false, scrollPosition: .none) // the "of" row should now be highlighted, regardless if the data changed order
}
甜味超标? 2024-08-20 06:30:39

SWIFT 3

self.collectionView.reloadData()
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])

SWIFT 3:

self.collectionView.reloadData()
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: [])
轻拂→两袖风尘 2024-08-20 06:30:39

我通过声明一个数组来存储所选单元格的 ID 或字符串文本,并将其添加到 didSelectItemAtIndexPath 函数中,从而更好地解决了这个问题。
这样,即使行数发生变化,所选行也不会发生变化。
例如:

var selectedItems: [String] = [] // This array type really depends on your preference and what property you're using to store
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let cell = collectionView.cellForItem(at: indexPath) else { return }
    selectedItems.append(cell.textField.text)
}

在您的初始 cellForItemAtIndexPath 上

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath)
    if let text = cell.textField.text {
        if selectedItems.contains(text) {
            collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
        }
    }
    return cell
}

I solved it better by declaring an array to store, let's say, the ID or String Text of the cell you have selected, and adding it on the didSelectItemAtIndexPath function.
This way, even if the number of rows changes, the selected ones won't.
For example:

var selectedItems: [String] = [] // This array type really depends on your preference and what property you're using to store
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    guard let cell = collectionView.cellForItem(at: indexPath) else { return }
    selectedItems.append(cell.textField.text)
}

And on your initial cellForItemAtIndexPath

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath)
    if let text = cell.textField.text {
        if selectedItems.contains(text) {
            collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
        }
    }
    return cell
}
标点 2024-08-20 06:30:39

我对 UICollectionView 的两分钱:(归功于 Basil..)

当我从后台任务重新加载时,我确实在主线程中运行:

final override func safeReloadData(){

            DispatchQueue.main.async { [weak self] in

                guard let self = self else {
                    return
                }

                let indexPaths = self.collectionView.indexPathsForSelectedItems
                self.collectionView.reloadData()
                for path in indexPaths ?? [] {
                    self.collectionView.selectItem(at: path, animated: false, scrollPosition: [])
                }
            }
    } 

my two cents for UICollectionView: (credits to Basil..)

I do run in main thread as I reload from a background task:

final override func safeReloadData(){

            DispatchQueue.main.async { [weak self] in

                guard let self = self else {
                    return
                }

                let indexPaths = self.collectionView.indexPathsForSelectedItems
                self.collectionView.reloadData()
                for path in indexPaths ?? [] {
                    self.collectionView.selectItem(at: path, animated: false, scrollPosition: [])
                }
            }
    } 
十秒萌定你 2024-08-20 06:30:39

听起来您没有使用数据的“模型” - 而只是更新“视图”(用户界面),因此可能是一个糟糕的设计。

reloadData 应该导致视图使用模型中的数据进行更新,该模型应该包含要显示的最新数据。

搜索有关“模型视图控制器模式”的资源

It sounds like you are not using a 'model' for the data - rather simply updating the 'view' (user interface), and thus is probably a bad design.

reloadData should cause the view to be updated with data from the model, which should contain the most current data to be displayed.

search for resources on the 'model view controller pattern'

独夜无伴 2024-08-20 06:30:38

简单的方法是这样的:

NSIndexPath *ipath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:ipath animated:NO scrollPosition:UITableViewScrollPositionNone];

The easy way is something like this:

NSIndexPath *ipath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:ipath animated:NO scrollPosition:UITableViewScrollPositionNone];
愿与i 2024-08-20 06:30:38

在 Marco 的答案中添加一点:

首先,如果您使用多重选择,您可以将此方法添加到您的 ViewController 中,并在需要调用 时调用它[tableView reloadData]

- (void)reloadTableView
{
    NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
    [self.tableView reloadData];
    for (NSIndexPath *path in indexPaths) {
        [self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    }
}

其次,如果您在 UITableViewController 中,并且希望在 tableView 出现后保留选择,UITableViewController 中有一个功能:clearsSelectionOnViewWillAppear 您可以打开或关闭。

请参阅此处: http://developer.apple .com/library/ios/#documentation/uikit/reference/UITableViewController_Class/Reference/Reference.html

Adding a bit to Marco's answer:

First, if you're using multiple selection, you can add this method to your ViewController and call it whenever you need to call [tableView reloadData]:

- (void)reloadTableView
{
    NSArray *indexPaths = [self.tableView indexPathsForSelectedRows];
    [self.tableView reloadData];
    for (NSIndexPath *path in indexPaths) {
        [self.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    }
}

Second, if you're in a UITableViewController and you want to preserve selection after tableView appears there's a feature in UITableViewController: clearsSelectionOnViewWillAppear you can turn on or off.

See here: http://developer.apple.com/library/ios/#documentation/uikit/reference/UITableViewController_Class/Reference/Reference.html

无畏 2024-08-20 06:30:38

已测试 Swift 4.2

重新加载表视图后更新所选行的正确方法是:

let selectedRows = tableView.indexPathsForSelectedRows

tableView.reloadData()

DispatchQueue.main.async {
    selectedRows?.forEach { selectedRow in
        tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
    }
}

Swift 4.2 Tested

The correct way to update selected rows after reload table view is:

let selectedRows = tableView.indexPathsForSelectedRows

tableView.reloadData()

DispatchQueue.main.async {
    selectedRows?.forEach { selectedRow in
        tableView.selectRow(at: selectedRow, animated: false, scrollPosition: .none)
    }
}
旧伤慢歌 2024-08-20 06:30:38

解决方法是使用 reloadSections: 而不是 reloadData。由于某种原因 reloadData 删除了当前选择。

The workaround is to use reloadSections: instead of reloadData. For some reason reloadData removes the current selection.

蒲公英的约定 2024-08-20 06:30:38

添加延迟对我来说不起作用(在 iOS 8.4 和 iOS 9 上测试)。有效的方法是在调用 -selectRowAtIndexPath:animated:scrollPosition: 之后,在选定的单元格上添加对 -layoutIfNeeded 的调用。

NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[[self.tableView cellForRowAtIndexPath:selectedIndexPath] layoutIfNeeded];

Adding a delay didn't work for me (tested on iOS 8.4 and iOS 9). What did work was adding a call to -layoutIfNeeded on the selected cell, after calling -selectRowAtIndexPath:animated:scrollPosition:.

NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
[self.tableView reloadData];
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[[self.tableView cellForRowAtIndexPath:selectedIndexPath] layoutIfNeeded];
著墨染雨君画夕 2024-08-20 06:30:38

在 iOS 9.3 和 Swift 2.x 上,我只需在主线程上调用该函数:)

self.tableView?.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)

On iOS 9.3 and Swift 2.x, I simply had to call the function on the main thread :)

self.tableView?.selectRowAtIndexPath(indexPath, animated: false, scrollPosition: .None)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文