Mac Catalyst:tableView 允许多重选择不起作用

Posted

技术标签:

【中文标题】Mac Catalyst:tableView 允许多重选择不起作用【英文标题】:Mac Catalyst: tableView allowmultipleselection not working 【发布时间】:2020-07-06 11:00:51 【问题描述】:

我有一个允许多选的表格视图。我在 viewDidLoad 中将allowMultipleSelection 和allowsMultipleSelectionDuringEditing 都设置为true,这在ios 和iPadOS 上都能完美运行。我决定今天试用 Catalyst,该应用程序看起来不错,只是我无法在此视图中选择多行。有任何想法吗?这是下面的代码。非常感谢。

//允许多选

override func viewDidLoad() 

    super.viewDidLoad()

    self.tableView.allowsMultipleSelection = true
    self.tableView.allowsMultipleSelectionDuringEditing = true
.....

//限制选择为7行

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? 
    if let selectedItems = tableView.indexPathsForSelectedRows 

        if selectedItems.count > 6 
            return nil
        
    
    return indexPath


@IBAction func doneButtonTapped(_ sender: UIBarButtonItem) 

...

    let selectedIndexPaths = tableView.indexPathsForSelectedRows
    if !selectedIndexPaths!.isEmpty 
        for index in selectedIndexPaths! 
            let selectedProcedure = fetchedResultsController?.object(at: index) as! Item
...

Rest of code to perform the required task

【问题讨论】:

【参考方案1】:

macOS Catalyst 上的多项选择与在 iOS 和 iPadOS 上的工作方式不同,这似乎是一个错误,或者是对预期行为的不幸选择。

在 macOS Catalyst 上,如果您通过将 tableView.allowsMultipleSelectionDuringEditing 设置为 true 来启用编辑模式下的多选,则一次只能通过指针单击直接选择一行。但是,通过选择第一行然后在选择第二行时按住 SHIFT 来启用连续行的多选,通过选择第一行然后在选择其他行时按住 COMMAND 来启用非连续行的多选。这是类似于 Mac 的行为,因为它是多选通常在 macOS 上的工作方式。因此,这可能是预期的行为。但如果是这样的话,这是难以发现的行为,而不是 iOS/iPadOS 用户可能期望的,并且与 iOS 和 iPadOS 上的工作方式不同。它会导致其他问题 - 例如,在代码中,我有一个“全选”功能,能够从 iOS/iPadOS 上的代码中选择所有行,而此代码在 macOS Catalyst 上不起作用。

我对此提交了反馈。 GitHub 上有一个简单的项目WB2ISS/MultipleSelection 可以演示该问题。

【讨论】:

感谢您提供详细而清晰的答案,您的项目清楚地展示了这种行为。我也不同意这种方法,因为-正如您所说-很难发现,并且用户在他们的设备上的行为应该是一致的。我想唯一的出路是向苹果提交反馈并希望最好!再次感谢。 看起来 Apple 故意选择这种行为以与 macOS 行为保持一致,但是,在撰写本文时绝对没有关于它的文档。我们的应用看起来不像 Mac 应用,它看起来像 iPad 应用(因为 Catalyst)。我的观点是,用户将与应用程序进行交互,就好像它是 iPad 应用程序而不是 Mac 应用程序一样;因此,不会想到 command + click 来执行多选。 嗨 :) 在我的应用程序中,我不需要进入编辑模式,所以我只有 tableView.allowsMultipleSelection = true。这在 iOS 中可以很好地选择一个或多个单元格。但是在不能使用 Shift/Command+click 的 Catalyst 上……有人有幸在这种模式下选择多个单元格吗?谢谢! :)【参考方案2】:

虽然这里所说的一切都是真的,但有一种“简单”的方法可以破解这种行为。使用下面的代码,您将在 Mac 上获得与在 iOS/iPadOS 上相同的行为

#if targetEnvironment(macCatalyst)
func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? 
    if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) 
        tableView.deselectRow(at: indexPath, animated: false)
        return nil
    
    return indexPath


func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? 
    if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) 
        return nil
    
    return indexPath


func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool 
    // the mac sets isHighlighted of each other cell to false before selecting them again which leads to a flickering of the selection. Therefore go through the selected cells and highlight them here manually
    tableView.indexPathsForSelectedRows?.forEach  tableView.cellForRow(at: $0)?.isHighlighted = true 
    return true

#endif

【讨论】:

感谢分享这个解决方法;对我来说,每个选定的单元格"blinks" 每次选择/取消选择一个新单元格时;这也发生在你身上吗?您找到解决方法了吗? 我也找到了解决方法并相应地编辑了答案。你必须覆盖shouldHighlightRowAt 这是一个很好的解决方法。编辑结束时,我发现有必要删除为减轻闪光/闪烁而添加的高光。此外,当从代码中进行选择时,比如实现全选函数,问题仍然存在,因为.selectRow(at:animated:scrollPosition:) 没有调用willSelectRowAtwillDeselectRowAt 委托方法。尽管整体多选行为是预期的,但.selectRow(at:animated:scrollPosition:) 的当前行为显然是一个错误。希望它会在 macOS 11 中得到修复。【参考方案3】:

以下是@ph1lb4 提供的解决方案,打包为独立的class。重要的是,这个版本在选择行时会调用didSelectRowAt,这意味着依赖于didSelectRowAt 的子类不会中断。

import UIKit

// WORKAROUND:
// As of macOS 10.15 Catalina, multi-row selection in Catalyst apps is not
// intuitive. The user is expected to use the shift key to select multiple
// rows. See https://***.com/q/60856636/1306956 for more details.
open class CatalystWorkaroundTableViewController: UITableViewController 
    #if targetEnvironment(macCatalyst)
    override open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? 
        if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) 
            tableView.deselectRow(at: indexPath, animated: false)
            self.tableView(tableView, didSelectRowAt: indexPath)
            return nil
         else 
            return indexPath
        
    

    override open func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? 
        if let selectedRows = tableView.indexPathsForSelectedRows, selectedRows.contains(indexPath) 
            return nil
         else 
            return indexPath
        
    

    // WORKAROUND:
    // Catalyst de-highlights cells beofre selecting them again which results in flickering.
    override open func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool 
        for indexPath in tableView.indexPathsForSelectedRows ?? [] 
            tableView.cellForRow(at: indexPath)?.isHighlighted = true
        
        return true
    
    #endif

【讨论】:

【参考方案4】:

好消息!在 macOS Big Sur UITableView tableView.allowsMultipleSelection = true 就像在 iOS 中一样工作!快乐的时光!您还可以通过编程方式选择多个单元格!

【讨论】:

我正在使用 Big Sur,截至 2021 年 9 月,我的 Mac Catalyst 应用程序中仍然存在 OP 概述的行为。 @lukemmtt 您是否使用 Command 键来选择更多?还有范围的Shift键?我这里没有我的应用,所以我现在不能自己尝试,抱歉。 command 键和 shift 键工作得非常好——但我和大多数用户都不会想到为像我这样使用 iPad 习语的 Mac Catalyst 应用程序这样做(即不是“为 mac 优化” )。这里的其他答案一致认为,这种行为要么是错误,要么是 Apple 的错误决定。在将我的 iPad 应用程序移植到 Mac Catalyst 时,我希望所有用户交互都保持不变,因为它们看起来几乎相同。最终,我在上面实现了 Rudolf Adamkovič 的基于类的解决方案,它在恢复预期的用户体验方面很有魅力——点击选择/取消选择。 好的,我明白了。我的评论是它完全被破坏了,但后来它开始在大苏尔工作。我认为它是否符合 iPad 体验是另一个讨论:)【参考方案5】:

下面是objective-c中写的解决方案。

谢谢。 @ph1lb4

#if TARGET_OS_MACCATALYST
    
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 

    NSArray<NSIndexPath *> * selectedRows = [tableView indexPathsForSelectedRows];
    if ([selectedRows containsObject:indexPath]) 
        [tableView deselectRowAtIndexPath:indexPath animated:false];
        
        return nil;
    

    return indexPath;


- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 
    NSArray<NSIndexPath *> * selectedRows = [tableView indexPathsForSelectedRows];
    if ([selectedRows containsObject:indexPath]) 
        return nil;
    
    return indexPath;


- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath 
    NSArray<NSIndexPath *> * selectedRows = [tableView indexPathsForSelectedRows];
    for(NSIndexPath *index in selectedRows)
        [[tableView cellForRowAtIndexPath:index] setHighlighted:YES];
    
    
    return YES;


#else

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath 
    
    return indexPath;
    


- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath 
    return indexPath;



#endif

【讨论】:

以上是关于Mac Catalyst:tableView 允许多重选择不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 Mac Catalyst 上创建 AVAudioFile? (错误-54)

如何在 Mac 上的 Mac Catalyst 应用程序中使用钥匙串?

UIMarkupTextPrintFormatter 和 Mac Catalyst

Mac Catalyst textview 第一响应者

使用 Crashlytics 构建 Mac Catalyst

iOS_Mac Catalyst(iOS 设计规范翻译)