iOS_ TableView(1)

longlivedrgn·2022년 11월 13일
0

iOS 스터디

목록 보기
6/8
post-thumbnail

Table View Basics

  • 아래와 같이 tableview를 추가해주고, 그 안에 tableviewcell을 추가해준다.

  • tableviewcell의 identifier를 설정해준다.

  • tableview의 delegate datasource를 설정해준다.

  • 아래와 같이 viewcontroller를 설정해두고, list에 있는 string을 tableview에 띄어보기

class TableViewBasicsViewController: UIViewController {
    
    let list = ["iPhone", "iPad", "Apple Watch", "iMac Pro", "iMac 5K", "Macbook Pro", "Apple TV"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
}
  • 아래와 같이 tableviewdatasource를 설정할 수 있다.
extension TableViewBasicsViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        print("#1", #function)
        return list.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        print("#2",#function)
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        // cell의 text 값을 위의 list의 값으로 설정을 한다.
        cell.textLabel?.text = list[indexPath.row]
        return cell
    }   
}
  • 아래와 같이 tableview가 생성이된다.

Multi Section (1)

  • 아래와 같은 tableview를 만들어보자

  • tableviewcell을 파일을 만들자.

  • switch를 만들고 cell에 해당 switch를 넣어준다.

class SwitchTableViewCell: UITableViewCell {

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        // switch를 만들고
        let v = UISwitch(frame: .zero)
        accessoryView = v
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}
  • switch가 있는 cell에 클래스를 위에서 생성한 class로 설정해준다.

  • disclosure Indicator를 accessory로 설정할 있다. 그리고 각각의 cell의 identifier를 disclosure, switch, checkmark, action으로 설정을 한다.

  • 그리고 tableview의 delegate와 datasource를 설정해준다.

** section은 tableview 안에 있는 cell들의 모임!

// 그전에 list가 무엇으로 정의되어있는지 확인해보자 //

    let list = PhotosSettingSection.generateData()

그리고 PhotosSettingSection은 아래와 같이 정의되어있다.

import UIKit

enum CellType: String {
    case action
    case disclosure
    case `switch`
    case checkmark
}


class PhotosSettingItem {
    init(type: CellType, title: String, on: Bool = false, imageName: String? = nil) {
        self.type = type
        self.title = title
        self.on = on
        self.imageName = imageName
    }
    
    let type: CellType
    let title: String
    var on: Bool
    var imageName: String?
}


class PhotosSettingSection {
    init(items: [PhotosSettingItem], header: String? = nil, footer: String? = nil) {
        self.items = items
        self.header = header
        self.footer = footer
    }
    
    let items: [PhotosSettingItem]
    var header: String?
    var footer: String?
    
    static func generateData() -> [PhotosSettingSection] {
        return [
            PhotosSettingSection(items: [
                PhotosSettingItem(type: .disclosure, title: "Siri & Search", imageName: "magnifyingglass.circle.fill")
            ],
            header: "Allow Photos to Access"),
            
            PhotosSettingSection(items: [
                PhotosSettingItem(type: .switch, title: "Hidden Album", on: true)
            ],
            footer: "When enabled, the Hidden album will appear in the Albums tab, under Utilities."),
            
            // item이 두 개인 section이다.
            PhotosSettingSection(items: [
                PhotosSettingItem(type: .switch, title: "Auto-Play Videos", on: false),
                PhotosSettingItem(type: .switch, title: "Summarize Photos", on: true)
            ],
            header: "Photos Tab",
            footer: "The Photos tab shows every photo in your library in all views. You can choose compact, summarized views for Collections and Years."),
            
            PhotosSettingSection(items: [
                PhotosSettingItem(type: .action, title: "Reset Suggested Memories"),
                PhotosSettingItem(type: .switch, title: "Show Holiday Events", on: true)
            ],
            header: "Memories",
            footer: "You can choose to see holiday events for your home country."),
            
            PhotosSettingSection(items: [
                PhotosSettingItem(type: .checkmark, title: "Automatic", on: true),
                PhotosSettingItem(type: .checkmark, title: "Keep Originals", on: false)
            ],
            header: "Transfer to mac or PC",
            footer: "Automatically transfer photos and videos in a compitable format, or always transfer the original file without checking for compatibility.")
        ]
    }
}
  • 그러면 아래와 같이 MultiSectionTableViewViewController의 데이터 값을 넣어줄 수 있다.
extension MultiSectionTableViewViewController: UITableViewDataSource {
    // 섹션 갯수는?
    func numberOfSections(in tableView: UITableView) -> Int {
        return list.count
    }
    
    // 하나의 섹션에는 몇개의 cell이 존재하는가 설정하기
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return list[section].items.count
    }
    
    // 각 cell은 어떠한 data를 가지고 있는 가?
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let target = list[indexPath.section].items[indexPath.row]
        
        let cell = tableView.dequeueReusableCell(withIdentifier: target.type.rawValue, for: indexPath)
        
        
        switch target.type {
        case .disclosure:
            cell.textLabel?.text = target.title
            cell.imageView?.image = UIImage(systemName: target.imageName ?? "")
        case .switch:
            cell.textLabel?.text = target.title
            if let switchView = cell.accessoryView as? UISwitch {
                switchView.isOn = target.on
            }
        case .action:
            cell.textLabel?.text = target.title
        case . checkmark:
            cell.textLabel?.text = target.title
            // on이 true이면 .checkmark false이면 .none -> checkmark는 기본으로 제공해주는 accessory이다.
            cell.accessoryType = target.on ? .checkmark : .none
        }
        return cell
    }
  • 그러면 최종적으로 아래와 같이 view를 만들 수 있다.
  • 그리고 아래와 같이 grouped로 설정을 하고
  • 컬러를 아래와 같이 설정을 하면
  • 최종적으로 아래와 같이 view를 완성시킬 수 있다.

Multi Section (2)

    @IBOutlet weak var listTableView: UITableView!
// 다시 viewcontroller로 돌아올 때 회색 선택된 거 풀어주기
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        if let selected = listTableView.indexPathForSelectedRow {
            listTableView.deselectRow(at: selected, animated: true)
        }
    }

switch 조지기

    @objc func toggleHideAlbum(_ sender: UISwitch) {
        print(#function)
        list[1].items[0].on.toggle()
    }
switch target.type {
        case .disclosure:
            cell.textLabel?.text = target.title
            cell.imageView?.image = UIImage(systemName: target.imageName ?? "")
        case .switch:
            cell.textLabel?.text = target.title
            if let switchView = cell.accessoryView as? UISwitch {
                switchView.isOn = target.on
                
                // 먼저 기존 switchview에 설정된 switch의 기능을 삭제해준다.
                switchView.removeTarget(nil, action: nil, for: .valueChanged)
                
                // 그리고 각각의 switchview에 위에 미리 정의한 selector를 이어준다.
                
                // 먼저 section = 1 이고 셀이 첫번째 일 경우
                if indexPath.section == 1 && indexPath.row == 0 {
                    switchView.addTarget(self, action: #selector(toggleHideAlbum(_:)), for: .valueChanged)
                }
            }

Action Sheet 띄우기

    func showActionSheet() {
        let sheet = UIAlertController(title: nil, message: "Resetting will allow previously blocked people, places, dates, or holidays to once again be included in new Memories.", preferredStyle: .actionSheet)
        
        let resetAction = UIAlertAction(title: "Reset", style: .destructive, handler: nil)
        sheet.addAction(resetAction)
        
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
        sheet.addAction(cancelAction)
        
        if let pc = sheet.popoverPresentationController {
            if let tbl = view.subviews.first(where: { $0 is UITableView }) as? UITableView {
                if let cell = tbl.cellForRow(at: IndexPath(row: 0, section: 3)) {
                    pc.sourceView = cell
                    pc.sourceRect = tbl.frame
                }
            }
        }
        
        present(sheet, animated: true, completion: nil)
    }
extension MultiSectionTableViewViewController: UITableViewDelegate {
    // tableview에서 tab을 하면 아래와 같은 함수가 호출이 된다.
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 해당 셀을 탭하면 showActionsheet 함수 호출하기
        if indexPath.section == 3 && indexPath.row == 0 {
            showActionSheet()
            // 회색 강조 없애기
            tableView.deselectRow(at: indexPath, animated: true)
        }
    }
}

Separator

  • 셀과 셀 사이를 구분하는 선을 넣어보자!
  • 아래와 같이 storyboard를 통하여 설정할 수 있고, 코드로도 설정할 수 있다.
  • 전체 cell을 동일한 separator, inset으로 구성을 해보자
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 코드를 통하여 전체 cell을 아래와 같이 설정할 수 있다.
        listTableView.separatorStyle = .singleLine
        listTableView.separatorColor = UIColor.systemBlue
        listTableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        listTableView.separatorInsetReference = .fromCellEdges
    }
  • 개별 셀(특정 row)만을 설정하고 싶다면 아래와 같은 코드를 통하여 설정해준다.
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        // 개별 셀마다 inset을 설정해주자
        if indexPath.row == 1 {
            cell.separatorInset = UIEdgeInsets(top: 0, left: 30, bottom: 0, right: 0)
        } else if indexPath.row == 2 {
            cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 30)
        } else {
            // 기본 inset으로 초기화를 하기
            cell.separatorInset = listTableView.separatorInset
        }
        
        cell.textLabel?.text = list[indexPath.row % list.count]
        return cell
    }
  • 아래와 같이 빌드가 된다.

Table View Cell

  • cell의 스타일은 아래와 같이 4개의 종류가 존재한다.

  • 아래의 메소드는 cell의 textLabel을 코드에서 설정한 list의 값들로 설정하는 것이다.

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        // Cell의 textlabel을 설정해준다.
        cell.textLabel?.text = list[indexPath.row]
        return cell
    }
  • 그리고 아래의 코드는 cell을 선택할 때마다 반복적으로 호출이 되어 cell을 클릭하면 cell의 textlabel이 찍히는 코드이다.
extension TableViewCellViewController: UITableViewDelegate {
    // 셀을 선택할 때마다 반복적으로 호출이된다.
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // cell이 있는지 확인하기
        if let cell = tableView.cellForRow(at: indexPath){
            print(cell.textLabel?.text ?? "")
        }
    }
}
  • 아래와 같이 storyboard가 구성이되어있다고 하자.

  • 왼쪽의 storyboard를 listTableView로 Outlet 설정을 해놓는다.

  • 그리고 왼쪽의 cell을 오른쪽 detailviewcontroller에 show segue로 연결해준다.

** 그래서 최종적으로 cell을 선택하면 오른쪽 detailview가 show되게 만들어 볼것이며, 해당 view는 cell의 text를 띄운다.

  • detailviewcontroller는 아래와 같이 구성되어있다.
class DetailViewController: UIViewController {
    
    @IBOutlet weak var valueLabel: UILabel!
    
    var value: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        valueLabel.text = value
    }
}
  • 그리고 textviewcellviewcontroller에 prepare 함수를 추가해준다.
class TableViewCellViewController: UIViewController {
    
    @IBOutlet weak var listTableView: UITableView!
    
    let list = ["iPhone", "iPad", "Apple Watch", "iMac Pro", "iMac 5K", "Macbook Pro", "Apple TV"]
    
    // 새로운 화면으로 전환되기 전에 실행이된다.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let cell = sender as? UITableViewCell {
            
            // cell의 index를 indexpath에 넘겨준다.
            if let indexPath = listTableView.indexPath(for: cell){
                if let vc = segue.destination as? DetailViewController{
                // value 값을 넣어주기
                    vc.value = list[indexPath.row]
                }
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
    }
}

Standard Accesory View

  • 아래와 같이 tableviewcell은 accessory와 editing acc를 설정할 수 있다. editing acc의 경우, 편집 모드일 때의 accessory를 말한다.

  • 그러면 각 row마다 다른 accessory를 설정해보자.

extension AccessoryViewViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 5
    }
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        switch indexPath.row {
        case 0:
            cell.textLabel?.text = "Disclosure Indicator"
            cell.accessoryType = .disclosureIndicator
        case 1:
            cell.textLabel?.text = "Detail Button"
            cell.accessoryType = .detailButton
        case 2:
            cell.textLabel?.text = "Detail Disclosure Button"
            cell.accessoryType = .detailDisclosureButton
        case 3:
            cell.textLabel?.text = "Checkmark"
            cell.accessoryType = .checkmark
        default:
            cell.textLabel?.text = "None"
            cell.accessoryType = .none
        }
        return cell
    }
}

위와 같이 코드를 설정하면 아래와 같은 화면이 나오게 된다.

Custom Accessory View

  • 위에 설정한 tableviewcell 밑에 새로운 tableviewcell을 추가해주고 이름을 customcell로 설정한다.
  • 그리고 cocoatouch 파일을 새로 만들어 customtableviewcell을 만들어준다.
  • 그리고 아래와 같이 accessoryview를 만들어서 넣어준다.
import UIKit

class CustomAccessoryTableViewCell: UITableViewCell {

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
        let v = UIImageView(image: UIImage(systemName: "star"))
        accessoryView = v
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}
  • 그리고 AccessoryViewViewController에서 위에서 만든 customcell을 추가해준다.
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        if indexPath.row == 4 {
            return tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
        }
  • 그러면 아래와 같이 tableview가 만들어진다.

pushSegue와 modalSegue를 사용해서 화면 띄우기

  • 새로운 viewcontroller를 생성하고 show로 이어준다.

  • 연결해준 segue의 이름을 pushSegue로 설정해준다.

  • 똑같은 방법을 통하여 새로운 viewcontroller를 생성하고 modalSegue로 설정해준다.

  • 최종적으로 detailview(i 문양)를 눌렀을 때는 modalSegue를 실행하고 , 나머지를 선택했을 때는 pushSegue를 실행시켜보자.

extension AccessoryViewViewController: UITableViewDelegate{
    // 셀을 선택하였을때 호출이 된다.
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // segue를 실행한다.
        performSegue(withIdentifier: "pushSegue", sender: nil)
    }
    // 셀에 있는 detailview를 선택하였을 때 호출이된다.
    func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        performSegue(withIdentifier: "modalSegue", sender: nil)
    }
}

Self Sizing

  • 아래에 row height와 estimate를 변경해주면 cell의 크기가 변경이된다.

0개의 댓글