iOS APP - 메모 앱 #4

longlivedrgn·2022년 8월 19일
post-thumbnail

메모 편집

DetailViewController에서 편집을 할 수 있게 해보자. 그러려면 DetailViewController에서 ComposeViewController와의 관계를 설정해주어야된다.

  • 밑의 toolbar item과 navigation controller를 연결해 준다.

  • ComposeViewController에 editTarget 변수를 만든다.

var editTarget: Memo?
  • DetailViewController에 저장된 memo를 ComposeViewController의 editTarget에 저장한다.
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination.children.first as? ComposeViewController {
            vc.editTarget = memo
        }
    }
  • ComposeViewController에서 editTarget이 존재하면 메모 편집, 만약 없다면 새 메모를 띄우게 설정하자.
    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let memo = editTarget {
            navigationItem.title = "메모 편집"
            memoTextView.text = memo.content
        } else {
            navigationItem.title = "새 메모"
            memoTextView.text = ""
        }
    }
  • ComposeViewController에서 save를 누를 때에 편집내용이 DB에 저장되게 설정하자.
@IBAction func save(_ sender: Any) {
        if let target = editTarget {
            target.content = memo
            DataManager.shared.saveContext()
        } else {
            DataManager.shared.addNewMemo(memo)
        }
     }

변경된 내용이 DetailView에 업데이트되게 하기

  • 새로운 Notification memoDidChange를 생성해준다.
extension ComposeViewController {
    static let newMemoDidInsert = Notification.Name(rawValue: "newMemoDidInsert")
    static let memoDidChange = Notification.Name(rawValue: "memoDidChange")
}
  • 노티피케이션을 설정해준다!
        if let target = editTarget {
            target.content = memo
            DataManager.shared.saveContext()
            NotificationCenter.default.post(name: ComposeViewController.memoDidChange, object: nil)

        } else {
            DataManager.shared.addNewMemo(memo)
            NotificationCenter.default.post(name: ComposeViewController.newMemoDidInsert, object: nil)
        }
  • DetailViewController에 memoTableView 변수를 저장한다.
    @IBOutlet weak var memoTableView: UITableView!
  • observer를 해제해 준다.
    // 옵져버 해제
    var token: NSObjectProtocol?
    
    deinit {
        if let token = token {
            NotificationCenter.default.removeObserver(token)
        }
    }
  • 그리고 NotificationCenter의 주파수를 받으면 memoTableView를 reload한다.
    override func viewDidLoad() {
        super.viewDidLoad()
        
        token = NotificationCenter.default.addObserver(forName: ComposeViewController.memoDidChange, object: nil, queue: OperationQueue.main, using: { [weak self] (noti) in
            self?.memoTableView.reloadData()
        })

    }
}

편집 취소 확인

Sheet를 풀다운할 때에 사용자에게 취소를 할 지, 안 할 지를 정하기!

  • 편집 이전의 메모 내용을 저장하는 변수를 ComposeViewController에 생성한다.
    // 편집 이전의 메모 내용을 저장하는 변수 생성
    var originalMemoContent: String?
  • delegate 설정(memoTextView가 변했는 지 알아차리기 위하여) 및 originalMemoContent 설정하기
    override func viewDidLoad() {
        super.viewDidLoad()
        
        if let memo = editTarget {
            navigationItem.title = "메모 편집"
            memoTextView.text = memo.content
            originalMemoContent = memo.content
        } else {
            navigationItem.title = "새 메모"
            memoTextView.text = ""
        }
        
        // 델리게이트 설정하기
        memoTextView.delegate = self
    }
  • view가 나타나기 전에 delegate을 설정하고, 없어지기 전에 delegate을 해제한다.
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        navigationController?.presentationController?.delegate = self
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        navigationController?.presentationController?.delegate = nil
    }
  • textView가 변경될 때 호출되는 textViewDidChange를 사용한다.
extension ComposeViewController: UITextViewDelegate {
    
    // textView에서 text가 변경될 때마다 호출이 된다.
    func textViewDidChange(_ textView: UITextView) {
        if let original = originalMemoContent, let edited = textView.text {
            // 원본과 편집된 것이 같은 지 다른지를 판단하여 편집이 된것인지 체크를 할 수 있다.
            // 만약 다르다면(편집이 된 것이라면) isModalInPresentation -> true -> sheet를 내리는 것도 불가능하다.
            isModalInPresentation = original != edited
        }
    }
}
  • isModalInPresentation가 true일 때에 호출되는 함수인 presentationControllerDidAttemptToDismiss를 설정해준다. (경고창을 띄우기)
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
    // 만약 isModalInPresentation가 true라면 아래의 함수가 호출이 된다.
    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
        // 경고 창 띄우기
        let alert = UIAlertController(title: "알림", message: "편집한 내용을 저장할까요?", preferredStyle: .alert)
        
        // 확인 액션 만들기
        // 경고창에서 확인버튼을 누르면 클로져가 실행이 된다. -> save가 된다.
        let okAction = UIAlertAction(title: "확인", style: .default) { [weak self] (action) in
            self?.save(action)
        }
        alert.addAction(okAction)
        
        // 취소 액션 만들기
        // 경고창에서 취소버튼을 누르면 크로져가 실행이 된다.
        let cancelAction = UIAlertAction(title: "취소", style: .cancel) { [weak self] (action) in
            self?.close(action)
        }
        alert.addAction(cancelAction)
        
        present(alert, animated: true, completion: nil)
        
    }
}

참고) 출처 - Zedd
https://zeddios.tistory.com/831

1개의 댓글

comment-user-thumbnail
2023년 12월 15일

It's great to see a comprehensive breakdown of the process. By the way, if you're ever looking for additional resources or professional support for iOS app development, Cleveroad offers excellent services. Check them out at https://www.cleveroad.com/services/mobile-development/ios/.

답글 달기