본문 바로가기
아이폰 개발/Swift

swift coredata - feat : tableView storyBoard 코드로 띄우기

by 인생여희 2021. 3. 27.

swift coredata - feat : tableView storyBoard 코드로 띄우기

 

완성화면

글작성

 

 

리스트 화면

 

로그 화면

 

 

스토리보드

 

 

 

모델 관계

 

 

게시판 모델

 

 

로그 모델

 

ViewController.swift - 리스트 화면

 

import CoreData
import UIKit

class ViewController: UIViewController, UITableViewDelegate , UITableViewDataSource {
  
    
    @IBOutlet weak var tableView: UITableView!
    //데이터 소스 역할을 할 배열 변수
    lazy var list:[NSManagedObject] = {
        return self.fetch()
    }()
    
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.list.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        //데이터 가져오기
        let record = self.list[indexPath.row]
        let title = record.value(forKey: "title") as? String
        let contents = record.value(forKey: "contents") as? String
        
        //셀을 생성하고 값 대입
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
        cell.textLabel?.text = title
        cell.detailTextLabel?.text = contents
        
        return cell
    }
    
    //행 수정 타입 : 삭제
    func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        return .delete
    }
    //행 밀어서 수정 완료
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let object = self.list[indexPath.row]
        
        if self.delete(object: object){
            
            //코어 데이터에서 삭제되면 배열 목록과 테이블 뷰의 행도 삭제
            self.list.remove(at: indexPath.row)
            self.tableView.deleteRows(at: [indexPath], with: .fade)
            
        }
    }
    
    //행 클릭 + 팝업창 + 수정
    //행선택시 팝업창 띄우고 수정하기
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        print("didSelectRowAt 선택")
        
        //선택된 행에 대한 데이터 가져옴
        let object = self.list[indexPath.row]
        let title = object.value(forKey: "title") as? String
        let contents = object.value(forKey: "contents") as? String
        
        let alert = UIAlertController(title: "게시글 수정", message: nil, preferredStyle: .alert)
        
        alert.addTextField(configurationHandler: {
            $0.text = title
        })
        
        alert.addTextField(configurationHandler: {
            $0.text = contents
        })
        
        alert.addAction(UIAlertAction(title: "취소", style: .cancel, handler: nil))
        alert.addAction(UIAlertAction(title: "save", style: .default, handler: { (_) in
            
            guard let title = alert.textFields?.first?.text , let contents = alert.textFields?.last?.text else{
                return
            }
            
            //값을 수정하는 메소드 호출, 성공시 테이블 뷰 리로드
            let check = self.edit(object: object, title: title, contents: contents)
            
            if check == true {
                
                let cell = self.tableView.cellForRow(at: indexPath)
                cell?.textLabel?.text = title
                cell?.detailTextLabel?.text = contents
                
                //수정된 셀을 첫번째로 이동
                let firstIndexRow = IndexPath(item: 0, section: 0)
                self.tableView.moveRow(at: indexPath, to: firstIndexRow)
                
                //self.tableView.reloadData()
            }
        }))

        self.present(alert, animated: false, completion: nil)
        
    }
    
    //자세히 보기 - 악세사리
    func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
        
        print("악세사리 선택")
        
        //스토리보드 identifire 로 화면 전환
        let object = self.list[indexPath.row]
        //let uvc = self.storyboard?.instantiateViewController(identifier: "LogVC") as! LogVC
        let uvc = self.storyboard?.instantiateViewController(withIdentifier: "LogVC") as! LogVC
        uvc.board = object as? BoardMo
        self.show(uvc, sender: self)
        
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //델리게이트 + 데이터 소스 설정
        self.tableView.delegate = self
        self.tableView.dataSource = self
        
        //바 버튼 아이템 추가
        let addBtn = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(add(_:)))
        self.navigationItem.rightBarButtonItem = addBtn
        
    }

    
    //데이터 추가
    @objc func add(_ sender:Any){
        
        //팝업창
        let alert = UIAlertController(title: "게시글 등록", message: nil, preferredStyle: .alert)
        
        //팝업창에 텍스트 필드 추가
        alert.addTextField(configurationHandler: {
            $0.placeholder = "제목"
        })
        alert.addTextField(configurationHandler: {
            $0.placeholder = "내용"
        })
        
        //버튼 + 이벤트 1
        alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: nil))
        
        //버튼 + 이벤트 2
        alert.addAction(UIAlertAction(title: "저장", style: .default, handler: { (_) in
            
            //팝업창의 Alert의 텍스트 필드 값 가져오기
            guard let title = alert.textFields?.first?.text, let contents = alert.textFields?.last?.text else{
                return
            }
            
            //값을 저장, 성공하면 테이블 리로드
            if self.save(title: title, contents: contents) == true{
                print("저장완료")
                self.tableView.reloadData()
            }

        })) //alert.addAction - end
        
        self.present(alert, animated: false, completion: nil)
        
    }
    
    //전체 조회
    func fetch() -> [NSManagedObject] {
        
        //앱델리게이트 객체 참조
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        
        //관리 객체 컨텍스트 참조
        let context = appDelegate.persistentContainer.viewContext
        
        //요청 객체 생성
        //코어 데이터에서 데이터를 가져올 때 요청 사항을 정의한 NSFetchRequest 객체가 사용됩니다.
        let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Board")
        
        
        //정렬 속성 설정 - false 내림차순, true 오름차순
        let sort = NSSortDescriptor(key: "regdate", ascending: false)
        fetchRequest.sortDescriptors = [sort]
        
        
        //데이터 가져오기
        //fetch(_:) 메서드는 원하는 데이터를 한꺼번에 가져올 때 호출합니다.
        //이 메서드로 반환되는 관리 객체의 데이터는 실제로 참조하기 전에는 로드되지 않습니다.
        let result = try! context.fetch(fetchRequest)
        
        return result
        
    }

    //저장
    func save(title:String, contents:String) -> Bool {
        
        //앱델리게이트 객체 참조
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        
        //관리 객체 컨텍스트 참조
        let context = appDelegate.persistentContainer.viewContext
        
        //관리 객체 생성
        //관리 객체는 생성과 동시에 컨텍스트 관리하에 있어야 하기 때문에
        //아래와 같은 방법으로 객체를 생성한다.
        let object = NSEntityDescription.insertNewObject(forEntityName: "Board", into: context)
        
        object.setValue(title, forKey: "title")
        object.setValue(contents, forKey: "contents")
        object.setValue(Date(), forKey: "regdate")
        
        
        
        //Log 관리 객체 생성, 어트리뷰트 값 대입
        let logObject = NSEntityDescription.insertNewObject(forEntityName: "Log", into: context) as! LogMo
        logObject.regdate = Date()
        logObject.type = LogType.create.rawValue
        
        //게시글 객체의 logs 속성에 새로 생성된 로그 객체 추가
        (object as! BoardMo).addToLogs(logObject)
        
        
        
        
        //영구 저장소에 커밋되고 나면 , list 프로퍼티에 추가
        do {
            try context.save()
            
            //배열의 제일 뒤에 추가
            //self.list.append(object)
            self.list.insert(object, at: 0) //0번째 인덱스에 데이터 삽입
            
            return true
            
        } catch  {
            //동기화 실패하면 RollBack
            context.rollback()
            return false
        
        }
        
    }//func save() - end
    
    
    //파일 삭제
/*
     코어 데이터의 삭제 과정은
     컨텍스트에서 해당 데이터를 삭제하고
     컨텍스트의 변경 사항을 영구 저장소에 동기화합니다.
     이 과정은 삭제가 아니라 변경을 반영하는 과정이기 때문에 save() 메서드를 호출하게 됩니다.
*/
    func delete(object: NSManagedObject) -> Bool{
        
        //앱 델리게이트 객체 참조
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        
        //관리 객체 컨텍스트 참조
        let context = appDelegate.persistentContainer.viewContext
        
        //컨텍스트로부터 해당 객체 삭제
        context.delete(object)
        
        
        //영구 저장소에 커밋
        do {
            try context.save()
            return true
        } catch{
            context.rollback()
            return false
        }

    }
    
    //수정
    func edit(object:NSManagedObject , title:String , contents:String) -> Bool{
        
        //앱 델리게이트  객체 참조
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        
        //관리 객체 컨텍스트 참조
        let context = appDelegate.persistentContainer.viewContext
        
        //관리 객체의 값을 수정
        object.setValue(title, forKey: "title")
        object.setValue(contents, forKey: "contents")
        object.setValue(Date(), forKey: "regdate")
        
        
        //Log 관리 객체 생성, 어트리뷰트 값 대입
        let logObject = NSEntityDescription.insertNewObject(forEntityName: "Log", into: context) as! LogMo
        logObject.regdate = Date()
        logObject.type = LogType.edit.rawValue
        
        //게시글 객체의 logs 속성에 새로 생성된 로그 객체 추가
        (object as! BoardMo).addToLogs(logObject)
        
        //영구저장소에 커밋
        do {
            try context.save()
            self.list = self.fetch()
            return true
        } catch  {
            context.rollback()
            return false
        }
        
        
    }
    
    
}

 

 

LogVC.swift -로그화면

 

import CoreData
import UIKit

//로그 타입
public enum LogType : Int16{
    case create = 0
    case edit = 1
    case delete = 2
}

//확장
extension Int16{
    
    func toLogType() -> String {
        switch self {
        case 0:
            return "생성"
            
        case 1:
            return "수정"
            
        case 2:
            return "삭제"
            
            
        default:
            return ""
        }
    }
    
}

class LogVC: UITableViewController {

    var board : BoardMo! //게시글 정보를 전달 받을 변수
    
    
/*
     로그 목록을 읽어오기 위해서 fetch() 메서드를 호출하지 않아도 됩니다.
     전달받은 board 변수 내부에 로그 목록에 대한 참조가 이미 포함되어 있기 때문입니다.

     array 속성은 NSSet, NSOrderedSet 객체를 배열 형태로 제공합니다.
     logs 속성을 배열 형태로 처리해서 [LogMo] 타입으로 캐스팅합니다.
*/
    lazy var list : [LogMo]! = {
        return self.board.logs?.array as! [LogMo]
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.title = self.board.title ?? "없음.."

    }

    // MARK: - Table view data source


    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return self.list.count
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        
        let row = self.list[indexPath.row]
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "logcell")!
        cell.textLabel?.text = "\(row.regdate!)에 \(row.type.toLogType()) 되었습니다."
        cell.textLabel?.font = UIFont.systemFont(ofSize: 12)
        
        return cell
    }

  
}

 

 

 

coreDataTest.zip
0.05MB

 

참고

https://blog.naver.com/go4693/221398724820