[새싹 iOS] 3주차_코드

임승섭·2023년 8월 4일
0

새싹 iOS

목록 보기
8/45
post-thumbnail

ToDo List

TableViewController.swift

//
//  TodoTableViewController.swift
//  0801pj
//
//  Created by 임승섭 on 2023/08/01.
//

import UIKit



// 기능 정리
// 1. done 버튼 - 누르면 해당 섹션의 맨 아래로 이동. 더이상 수정 불가
// 2. star 버튼 - 선택o면 '즐겨찾기' 섹션으로 이동. 선택x면 '일반' 섹션으로 이동
// 3. textField - 텍스트 입력 후 return/ok버튼 누르면 리스트 추가. 왼쪽 pull down button으로 섹션 구분
// 4. searchBar - 텍스트 입력할 때마다 해당 텍스트 포함한 리스트 아래에 출력. 두 섹션 모두 적용


extension TodoTableViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        guard let txt = searchController.searchBar.text?.lowercased() else { return }
        
        self.filteredList.specialList = self.list.specialList.filter{ $0.main.lowercased().contains(txt) }
        self.filteredList.todoList = self.list.todoList.filter{
            $0.main.lowercased().contains(txt) }
            
        self.tableView.reloadData()
    }  
}


class TodoTableViewController: UITableViewController {
    
    var list = ToDoInformation()            // 전체 리스트
    var filteredList = ToDoInformation()    // 검색 시 나타나는 리스트
    
    // 현재 필터링이 필요한지 (즉, 검색중인지) 확인하는 함수
    var isFiltering: Bool {
        let searchController = self.navigationItem.searchController
        let isActive = searchController?.isActive ?? false
        let isSearchBarHasText = searchController?.searchBar.text?.isEmpty == false
        
        print(isActive && isSearchBarHasText)
        
        return isActive && isSearchBarHasText
    }
    
    // 프로토콜 setup
    func setupSearchController() {
        let searchController = UISearchController(searchResultsController: nil)
        self.navigationItem.searchController = searchController
        
        searchController.searchResultsUpdater = self
    }
    
    func setupTableView() {
        self.tableView.delegate = self
        self.tableView.dataSource = self
    }
    
    
    @IBOutlet var pullDownButton: UIButton!
    @IBOutlet var newTextField: UITextField!
    @IBOutlet var okButton: UIButton!
    
    // pull down button의 상태
    var option = Option.일반.rawValue


    override func viewDidLoad() {
        super.viewDidLoad()

        // nib 등록
        let nib = UINib(nibName: identifier.TodoTableViewCell.rawValue, bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: identifier.TodoTableViewCell.rawValue)
        
        // pull down button 디자인
        designPullDownButton(pullDownButton)
        
        // 프로토콜 setup
        setupTableView()
        setupSearchController()
    }
    
    // 0. 섹션 개수
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }
    
    // 0.5 섹션 헤더 이름
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return (section == 0) ? "즐겨찾기" : "일반"
    }
    
    // 1. 셀 개수
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        if (section == 0) {
            if (isFiltering) {
                return filteredList.specialList.count
            } else {
                return list.specialList.count
            }
        }
        else {
            if (isFiltering) {
                return filteredList.todoList.count
            } else {
                return list.todoList.count
            }
        }
    }
    
    // 2. 셀 데이터 및 디자인
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: identifier.TodoTableViewCell.rawValue) as! TodoTableViewCell
        
        
        if (indexPath.section == 0 /*&& !list.specialList.isEmpty*/) {
            if (isFiltering) {
                let row = filteredList.specialList[indexPath.row]  // type : ToDo
                cell.designCell(row)
            }
            else {
                let row = list.specialList[indexPath.row]  // type : ToDo
                cell.designCell(row)
            }
        }
        else if (indexPath.section == 1 /*&& !list.todoList.isEmpty*/ ){
            if (isFiltering) {
                let row = filteredList.todoList[indexPath.row]
                cell.designCell(row)
            }
            else {
                let row = list.todoList[indexPath.row]
                cell.designCell(row)
            }
        }
        
        // 버튼 기능 (섹션 구분을 어떻게 해줘야 할까) -> 클로저를 이용하는 방법
        // 1. done Button (해당 배열에서 remove 후, 맨 뒤에 다시 insert)
        cell.doneCallBackMethod = { [weak self] in
            
            // 누르는것만 작동하게 할거다. 취소 불가능
            if (indexPath.section == 0 && !((self?.list.specialList[indexPath.row].done)!)  ) {
                var tmp: ToDo = (self?.list.specialList[indexPath.row])!

                self?.list.specialList.remove(at: indexPath.row)
                
                tmp.done = true;
                
                self?.list.specialList.insert(tmp, at: (self?.list.specialList.count)!)
            }
            else if (indexPath.section == 1 && !((self?.list.todoList[indexPath.row].done)!)  ) {
                var tmp: ToDo = (self?.list.todoList[indexPath.row])!

                self?.list.todoList.remove(at: indexPath.row)
                
                tmp.done = true;
                
                self?.list.todoList.insert(tmp, at: (self?.list.todoList.count)!)
            }
            
            tableView.reloadData()
        }
        
        // 2. special Button (해당 배열에서 remove 후, 다른 배열에 insert)
        // 이미 done이면 이것도 작동 못함
        cell.specialCallBackMethod = { [weak self] in

            if (indexPath.section == 0 && !((self?.list.specialList[indexPath.row].done)!)) {   // 즐겨찾기 버튼이 눌러져 있다 (special == true)
                // specialList에서 빼고 todoList로 넣어준다
                var tmp: ToDo = (self?.list.specialList[indexPath.row])!

                self?.list.specialList.remove(at: indexPath.row)
                
                tmp.special = false;
                
                self?.list.todoList.insert(tmp, at: 0)
            }
            else if (indexPath.section == 1 && !((self?.list.todoList[indexPath.row].done)!)) {  // 즐겨찾기 버튼이 안눌러져 있다 (special == false)
                var tmp: ToDo = (self?.list.todoList[indexPath.row])!

                self?.list.todoList.remove(at: indexPath.row)

                tmp.special = true
                
                self?.list.specialList.insert(tmp, at: 0)
            }
            
            tableView.reloadData()
        }
        
        
        
        return cell
    }

   
    // design
    func designPullDownButton(_ sender: UIButton) {
        
        sender.setTitle(option, for: .normal)
        
        let op1 = UIAction(title: Option.즐겨찾기.rawValue) { _ in
            self.option = Option.즐겨찾기.rawValue
            sender.setTitle(self.option, for: .normal)
        }
        
        let op2 = UIAction(title: Option.일반.rawValue) { _ in
            self.option = Option.일반.rawValue
            sender.setTitle(self.option, for: .normal)
        }
        
        let buttonMenu = UIMenu(title: "선택", children: [op1, op2])
        
        sender.menu = buttonMenu
    }
    
    // check textfield (textfield에 입력이 올바른지 확인)
    func checkTextField(_ sender: UITextField) -> Bool {
        if let txt = sender.text {
            if txt.count >= 1 {
                return true
            }
        }
        
        return false
    }
    
    // update List
    func updateList() {
        if (checkTextField(newTextField)) {
            let txt = newTextField.text!
            
            if option == Option.일반.rawValue {
                list.todoList.insert(ToDo(done: false, main: txt, special: false), at: 0)
                
                tableView.reloadData()
                
                newTextField.text = ""
            }
            else if option == Option.즐겨찾기.rawValue {
                list.specialList.insert(ToDo(done: false, main: txt, special: true), at: 0)
                
                tableView.reloadData()
                
                newTextField.text = ""
                
            }
        }
        // 텍스트가 제대로 입력되지 않았으면 alert
        else {
            let alert = UIAlertController(title: "텍스트가 올바르지 않습니다", message: "다시 써주세요", preferredStyle: .alert)
            let ok = UIAlertAction(title: "확인", style: .default)
            alert.addAction(ok)
            present(alert, animated: true)
        }

    }
    
    // return or ok버튼이 눌리면 리스트 업데이트
    @IBAction func textfieldReturnTapped(_ sender: UITextField) {
        updateList()
    }
    
    @IBAction func okButtonTapped(_ sender: UIButton) {
        updateList()
        view.endEditing(true)
    }
}

TableViewCell.swift

//
//  TodoTableViewCell.swift
//  0801pj
//
//  Created by 임승섭 on 2023/08/01.
//

import UIKit

class TodoTableViewCell: UITableViewCell {
    
    var specialCallBackMethod: (() -> Void)?
    var doneCallBackMethod: (() -> Void)?
    
    
    @IBOutlet var doneButton: UIButton!
    @IBOutlet var specialButton: UIButton!
    @IBOutlet var todoTextView: UITextView!
    
    
    func designCell(_ sender: ToDo) {
        
        doneButton.setImage(UIImage(systemName: (sender.done)
                                    ? "checkmark.square.fill"
                                    : "checkmark.square")
                                    , for: .normal)
        specialButton.setImage(UIImage(systemName: (sender.special)
                                      ? "star.fill"
                                       : "star")
                               , for: .normal)
        
        todoTextView.text = sender.main
    }
    
    
    
    @IBAction func specialButtonTapped(_ sender: UIButton) {
        specialCallBackMethod?()
    }
    
    @IBAction func doneButtonTapped(_ sender: UIButton) {
        doneCallBackMethod?()
    }
    
}

Movie List

ViewController

//
//  BookViewController.swift
//  0802hw
//
//  Created by 임승섭 on 2023/08/02.
//



import UIKit

// 기능 정리
// 1. 위는 collectionView, 아래는 tableView
// 2. tableView의 헤더에 collectionView
// 3. tableView에는 모든 데이터, collectionView에는 좋아요 누른 데이터

class BookViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UICollectionViewDelegate, UICollectionViewDataSource {
    
    
    @IBOutlet var bookCollectionView: UICollectionView!
    @IBOutlet var bookTableView: UITableView!
    
    // 데이터 저장
    var data = MovieInfo().movie
    
    /* tableView */
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "요즘 인기 작품"
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return data.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: Cell.BookTableViewCell.rawValue) as! BookTableViewCell
        
        cell.designCell(data[indexPath.row])
        
        // (tableView) 좋아요 버튼을 눌렀을 때 -> data의 해당 요소 toggle
        cell.heartCallBackMethod = { [weak self] in
            self?.data[indexPath.row].like.toggle()
            
            self?.bookTableView.reloadData()
            self?.bookCollectionView.reloadData()
        }
        
        return cell
    }
    
    /* collectionView */
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data.filter { $0.like }.count    // filter 이용
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Cell.BookCollectionViewCell.rawValue, for: indexPath) as! BookCollectionViewCell
        
        cell.designCell(data.filter { $0.like }[indexPath.row] )
        
        cell.heartCallBackMethod = { [weak self] in
            // 배열을 따로 안만들어서 여기서 좀 막힌다
            // 현재 여기 있는 애들은 filter를 거쳐서 나온 새로운 배열.
            // 여기서의 indexPath로 기존 배열에 접근해야 하는데 불가능
            
            // 일단 방법 1. 현재 요소의 제목을 따로 저장해서 기존 배열에 맞는 제목을 검색해서 찾음... (시간적으로 아주 비효율적)
            // struct 끼리도 == 연산 사용 가능한가?
            
            let title = self?.data.filter{ $0.like }[indexPath.row].title // 아직 좋아요가 토글되기 전 좋아요 배열에서 해당 영화의 타이틀
            
            for i in 0...(self?.data.count)! - 1 {
                if title == self?.data[i].title {
                    self?.data[i].like.toggle()
                }
            }
            
//            // 아니 얘는 왜안되냐 -> 되네?
//            for var m in (self?.data)! {
//                if title == m.title {
//                    m.like.toggle()
//                }
//            }
            // 이렇게 하면 아무 의미없는 코드가 되어버림;;
            
            
            self?.bookCollectionView.reloadData()
            self?.bookTableView.reloadData()
            
            self?.configureCollectionViewLayout()
        }
        return cell
    }
    
    // 셀 선택 시 상세화면으로
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 1 + 2. 같은 스토리보드 내 뷰컨
        let vc = storyboard?.instantiateViewController(withIdentifier: Cell.DetailViewController.rawValue) as! DetailViewController
        
        vc.contents = data[indexPath.row]
        
        let nav = UINavigationController(rootViewController: vc)
        
        nav.modalPresentationStyle = .fullScreen
        
        present(nav, animated: true)
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let vc = storyboard?.instantiateViewController(withIdentifier: Cell.DetailViewController.rawValue) as! DetailViewController
        
        vc.contents = data.filter{ $0.like }[indexPath.row]
        
        let nav = UINavigationController(rootViewController: vc)
        
        nav.modalPresentationStyle = .fullScreen
        
        present(nav, animated: true)
    }
    

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 등록
        let nib = UINib(nibName: Cell.BookTableViewCell.rawValue, bundle: nil)
        bookTableView.register(nib, forCellReuseIdentifier: Cell.BookTableViewCell.rawValue)
        
        let nib2 = UINib(nibName: Cell.BookCollectionViewCell.rawValue, bundle: nil)
        bookCollectionView.register(nib2, forCellWithReuseIdentifier: Cell.BookCollectionViewCell.rawValue)
        
        // 프로토콜 연결
        bookCollectionView.dataSource = self
        bookCollectionView.delegate = self
        
        bookTableView.dataSource = self
        bookTableView.delegate = self
        
        configureCollectionViewLayout()
        bookTableView.rowHeight = 150
    }
    
    
    
    // configure cell
    func configureCollectionViewLayout() {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: 100, height : 150)
        layout.sectionInset = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
        
        bookCollectionView.collectionViewLayout = layout
    }
}

TableViewCell

//
//  BookTableViewCell.swift
//  0802hw
//
//  Created by 임승섭 on 2023/08/02.
//

import UIKit

class BookTableViewCell: UITableViewCell {
    
    var heartCallBackMethod: ( () -> Void )?
    
    @IBOutlet var posterImageView: UIImageView!
    @IBOutlet var titleLabel: UILabel!
    @IBOutlet var heartButton: UIButton!
    @IBOutlet var subLabel: UILabel!
    
    func designCell(_ sender: Movie) {
        posterImageView.image = UIImage(named: sender.title)
        posterImageView.contentMode = .scaleAspectFit
        
        titleLabel.text = sender.title
        
        subLabel.text = "\(sender.releaseDate) | \(sender.rate)"
        
        heartButton.setImage(UIImage(systemName: (sender.like) ? "heart.fill" : "heart"),
                             for: .normal)
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

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

        // Configure the view for the selected state
    }
    
    
    
    @IBAction func heartButtonTapped(_ sender: UIButton) {
        heartCallBackMethod?()
    }
}

0개의 댓글