아래와 같이 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()
}
}
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
}
}
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.")
]
}
}
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
}
@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)
}
}
@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)
}
}
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)
}
}
}
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
}
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
}
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
}
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를 띄운다.
class DetailViewController: UIViewController {
@IBOutlet weak var valueLabel: UILabel!
var value: String?
override func viewDidLoad() {
super.viewDidLoad()
valueLabel.text = value
}
}
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()
}
}
아래와 같이 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
}
}
위와 같이 코드를 설정하면 아래와 같은 화면이 나오게 된다.
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
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 4 {
return tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath)
}
새로운 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)
}
}