Handling Row Selection in a Table View

Panther·2021년 8월 28일
0

https://developer.apple.com/documentation/uikit/uitableviewdelegate/handling_row_selection_in_a_table_view

"Detect when a user taps a table view cell so your app can take the next indicated action."

사용자가 테이블 뷰 셀을 탭할 때 이를 감지하고 앱이 그에 상응하는 다음 액션을 수행할 수 있게 합니다.

Overview

사용자가 테이블 뷰의 행을 탭하면 다른 뷰가 슬라이드해서 위치한다거나 체크마크로 선택된 상태를 행이 나타나게 되는 것처럼 몇 가지 종류의 액션이 따라오도록 할 수 있습니다. 앱이 액션을 수행하기 위해서 사용자가 행을 탭하는 시점과 그에 따라 응답할 수 있는 시점을 알아야 합니다.

Configure Row Selection Behavior

행 선택이 어떻게 동작할지를 설정하기 위해 테이블 뷰에서 아래 속성을 사용하시기 바랍니다.

allowsSelection
테이블이 편집 모드가 아닐 때 사용자가 행을 선택할 수 있을지를 결정합니다. 기본값은 true입니다.

allowsMultipleSelection
테이블이 편집 모드가 아닐 때 사용자가 둘 이상의 행을 선택할 수 있을지를 결정합니다. 기본값은 false입니다.

allowsSelectionDuringEditing
테이블 뷰가 편집 모드인 동안 사용자가 행을 선택할 수 있을지를 결정합니다. 기본값은 false입니다.

allowsMultipleSelectionDuringEditing
테이블 뷰가 편집 모드인 동안 사용자가 둘 이상의 행을 선택할 수 있을지를 결정합니다. 기본값은 false입니다.

Respond to Row Selections

사용자가 행을 탭하면 테이블 뷰는 딜리게이트 메소드인 tableView(_:didSelectRowAt:)을 호출합니다. 이 시점에 앱은 선택된 하이킹 트레일의 세부사항을 표시하는 것과 같은 액션을 수행합니다.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let selectedTrail = trails[indexPath.row]
    
    if let viewController = storyboard?.instantiateViewController(identifier: "TrailViewController") as? TrailViewController {
        viewController.trail = selectedTrail
        navigationController?.pushViewController(viewController, animated: true)
    }
}

네비게이션 스택에 새로운 뷰 컨트롤러를 넣는 것으로 셀 선택에 응답하려면, 뷰 컨트롤러가 스택에서 빠져나올 때 셀을 선택해제해야 합니다. 테이블 뷰 표시를 위해 UITableViewController를 사용하고 있다면 clearsSelectionOnViewWillAppear 속성을 true로 설정해서 이 동작을 가져올 수 있습니다. 반대로 뷰 컨트롤러의 viewWillAppear(_:) 메소드에서 선택을 지울 수 있습니다.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if let selectedIndexPath = tableView.indexPathForSelectedRow {
        tableView.deselectRow(at: selectedIndexPath, animated: animated)
    }
}

행이 세부 버튼 액세서리 뷰를 표시하고 사용자가 이를 탭하면 테이블 뷰는 tableView(_:didSelectRowAt:) 메소드 대신 tableView(_:accessoryButtonTappedForRowWith:) 메소드를 호출합니다. 액세서리 뷰와 관련한 더 많은 정보는 Configuring the Cells for Your Table을 보시기 바랍니다.

Configuring the Cells for Your Table
https://developer.apple.com/documentation/uikit/views_and_controls/table_views/configuring_the_cells_for_your_table
https://velog.io/@panther222128/Configuring-the-Cells-for-Your-Table

Select a Row Programmatically

행의 선택은 테이블 뷰에서의 탭이 아닌 앱 자체 내부에서 기인할 것입니다. 예를 들어 사용자는 주소록에 새로운 사람을 추가할 것이고, 이후 연락처의 리스트를 반환할 것입니다. 연락처 리스트를 반환한 후 앱은 새로 추가된 사람의 행을 보여주기 위해 리스트를 스크롤합니다. 이와 같은 상황에서 새로운 행을 선택하고 스크롤하기 위해 테이블 뷰 메소드인 selectRow(at:animated:scrollPosition:)을 사용할 수 있습니다.

Note
코드 작성으로 행을 선택하는 것은 딜리게이트 메소드인 tableView(_:willSelectRowAt:) 혹은 tableView(_:didSelectRowAt:)을 호출하지 않고, 옵저버에 selectionDidChangeNotification 노티피케이션을 보내지도 않습니다.

Manage Selection Lists

선택된 아이템의 리스트를 포괄적이고 배타적인 상태로 유지하기 위해 셀 선택을 사용할 수 있습니다. 포괄적 리스트는 하나 혹은 하나 이상의 선택된 아이템을 가질 수 있는 반면 배터직 리스트는 최대 하나만을 갖습니다.

앱에서 선택 리스트를 제공할 때 아이템의 상태를 나타내려고 할 때 셀의 선택된 상태를 사용하지 않아야 합니다. 체크마크를 사용해서 상태를 나타내려면 tableView(_:didSelectRowAt:) 딜리게이트 메소드를 구현하고, 다음으로 셀의 accessoryType 속성을 UITableViewCell.AccessoryType.checkmark로 설정해서 따라오는 deselectRow(at:animated:)로 셀을 선택해제해야 합니다.

예를 들어 여행자를 위한 앱은 사용자가 여행 가방에 담아야 하는 물픔 리스트를 생성하도록 해줍니다(취침 가방 및 텐트와 같은 캠핑 기어의 포괄적 리스트). 사용자가 기어의 부분을 짐에 넣는 것을 나타내는 아이템을 탭하면, 앱은 행을 선택해제하고 아이템의 데이터 모델을 업데이트하며, 가방에 들어간 아이템에 대한 행에서 체크마크를 표시합니다. 아이템을 다시 탭하면 가방에 들어가지 않은 아이템으로 표시하고 체크마크를 제거합니다. 아래가 예시입니다.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // Unselect the row, and instead, show the state with a checkmark.
    tableView.deselectRow(at: indexPath, animated: false)
    
    guard let cell = tableView.cellForRow(at: indexPath) else { return }
    
    // Update the selected item to indicate whether the user packed it or not.
    let item = packingList[indexPath.row]
    let newItem = PackingItem(name: item.name, isPacked: !item.isPacked)
    packingList.remove(at: indexPath.row)
    packingList.insert(newItem, at: indexPath.row)
    
    // Show a check mark next to packed items.
    if newItem.isPacked {
        cell.accessoryType = .checkmark
    } else {
        cell.accessoryType = .none
    }
}

배타적 리스트 관리도 유사합니다. 행을 선택해제하고, 선택된 상태를 나타내는 체크마크 혹은 액세서리 뷰를 표시할 수 있습니다. 포괄적 리스트와 다르게 배타적 리스트는 한 번에 하나의 선택된 아이템만을 허용합니다.

예를 들어 여행자의 앱은 사용자가 난이도(easy, moderate, hard)에 기반해 하이킹 트레일의 리스트를 거를 수 있도록 해줍니다. 이러한 종류의 배타적 리스트를 사용하면 앱은 이전 선택으로부터 체크마크를 제거해야 하고, 현재 선택에 대한 체크마크를 표시해야 합니다. 앱은 어떤 아이템이 현재 선택된 아이템인지를 기억해야 합니다. 예를 들면 아래와 같습니다.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // Unselect the row.
    tableView.deselectRow(at: indexPath, animated: false)
    
    // Did the user tap on a selected filter item? If so, do nothing.
    let selectedFilterRow = selectedFilters[indexPath.section]
    if selectedFilterRow == indexPath.row {
        return
    }

    // Remove the checkmark from the previously selected filter item.
    if let previousCell = tableView.cellForRow(at: IndexPath(row: selectedFilterRow, section: indexPath.section)) {
        previousCell.accessoryType = .none
    }
    
    // Mark the newly selected filter item with a checkmark.
    if let cell = tableView.cellForRow(at: indexPath) {
        cell.accessoryType = .checkmark
    }
    
    // Remember this selected filter item.
    selectedFilters[indexPath.section] = indexPath.row
}

0개의 댓글