포스트

3주차 과제 (8)

이번글은 참고자료를 위주로 따라한것밖에 없는듯 하다.

주말에 제대로 하나씩 뜯어보면서 봐야할듯하다

과제기간이 얼마 남지 않은 만큼 어쩔수가 없는게 아쉽다

Lv4

1. Calendar 기능 추가하기

1. 새로운 VC 파일 생성

생략

2. Sidebar의 구조 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private func addDimmingView() {
        dimmingView = UIView(frame: self.view.bounds)
        dimmingView?.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        dimmingView?.isHidden = true
        view.addSubview(dimmingView!)
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleDimmingViewTap))
        dimmingView?.addGestureRecognizer(tapGesture)
    }
    
    @objc private func handleDimmingViewTap() {
        let sideBarVC = self.sideBarViewController
        
        UIView.animate(withDuration: 0.3, animations: {
            // 사이드 메뉴를 원래 위치로 되돌림.
            sideBarVC.view.frame = CGRect(x: -self.view.frame.width, y: 0, width: self.view.frame.width, height: self.view.frame.height)
            // 어두운 배경 뷰를 숨김.
            self.dimmingView?.alpha = 0
        }) { (finished) in
            // 애니메이션이 완료된 후 사이드 메뉴를 뷰 계층 구조에서 제거.
            sideBarVC.view.removeFromSuperview()
            sideBarVC.removeFromParent()
            self.dimmingView?.isHidden = true
        }
    }
    
    @IBAction func openSideBtn(_ sender: UIBarButtonItem) {
        let sideBarVC = self.sideBarViewController
        
        // 사이드 메뉴 뷰 컨트롤러를 자식으로 추가하고 뷰 계층 구조에 추가.
        self.addChild(sideBarVC)
        self.view.addSubview(sideBarVC.view)
        
        // 사이드 메뉴의 너비를 화면 너비의 70%로 설정.
        let menuWidth = self.view.frame.width * 0.70
        let menuHeight = self.view.frame.height
        let yPos = (self.view.frame.height / 2) - (menuHeight / 2) // 중앙에 위치하도록 yPos 계산
        
        
        // 사이드 메뉴의 시작 위치를 화면 왼쪽 바깥으로 설정.
        sideBarVC.view.frame = CGRect(x: -menuWidth, y: yPos, width: menuWidth, height: menuHeight)
        
        // 어두운 배경 뷰를 보이게 합니다.
        self.dimmingView?.isHidden = false
        self.dimmingView?.alpha = 0
        
        UIView.animate(withDuration: 0.3, animations: {
            // 사이드 메뉴를 화면에 표시.
            sideBarVC.view.frame = CGRect(x: 0, y: yPos, width: menuWidth, height: menuHeight)
            // 어두운 배경 뷰의 투명도를 조절.
            self.dimmingView?.alpha = 1
        })
        
    }

참고자료 보고 작성한 글이다 보니 아직 제대로된 이해를 하지못하였다.

아무래도 주말에 해당부분을 완벽하게 이해하여, 프로젝트를 다음주부터할때 지장이 없게끔 해야할듯하다.

2. Calendar에 표시하기.

참고자료 를 보고 작성을 해본다.

iOS 16부터 구현된 기능이라고 한다.

작동화면

3. CellDetailVC에 오늘 할일 등록하여 DB연동시키기

1. DatePicker 구현

DatePicker를 통해 날짜를 선택하면 그 날짜 값을 DB에 저장하려고 한다.

우선 DatePicker에서 어떤 값을 리턴을 해야할지 생각을 해보았다.

여러기능을 구현하는건 도움이 되겠지만 너무 막 만드는건 아니다 싶어

날짜를 지정해서 보여주는게 좋다고 판단했다.

1
2
3
4
5
6
7
8
9
 @IBAction func setDate(_ sender: UIDatePicker) {
        
       let datePicker = sender
        
        let dateFormat = DateFormatter()
        dateFormat.dateFormat = "yyyy-MM-dd"
        print(dateFormat.string(from: datePicker.date))
        print(dateFormat.string(from: Date()))
    }

출력을 하면

2024-03-28 → 28일을 선택한경우

2024-03-27 → 오늘날짜 print한경우

이런식으로 나온다

2. 새로운 Field 추가

DB에 새로운 Attribute가 추가해야하므로 ToDoModel모델로 가주었다.

1
2
3
4
5
6
7
8
9
10
struct ToDoModel {
    
    var id : Int // 게시글 번호
    var title : String // 제목
    var isComplete : Bool // 다 끝냈는지?
    var isFav : Bool // 즐겨찾기
    var imageTitle : String // 이미지 업로드시 가질 이름
    var date : String // 날짜 지정 new!
    
}

그리고 문자열을 관리해주는 Constants에도 새로 추가를 해주자

늘 그렇듯 문자열은 내가만든 ToDoModel 명을 그대로 사용하는걸 잊지말자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct Constants {
    
    static var cellIdentifier = "ToDoCell"
    static var cellName = "ToDoListCell"
    static var collectionName = "ToDoList"
    
    struct Fire {
        static var fireId = "id"
        static var fireTitle = "title"
        static var fireBool = "isComplete"
        static var docuName = "List"
        static var favBool = "isFav"
        static var fireImageTitle = "imageTitle"
        static var fireDate = "date" //new
    }
    
    struct Symbol {
        static var star = "star"
        static var fillStar = "star.fill"
    }
}

시간으로해서 디테일있게 하는것보다는 날짜로만 지정해주는게 더 좋다고 판단했다.

물론 시작날짜, 끝맺음날짜도 가능하지만, 그렇게 했을때 어떻게 그걸 표현을 할까에 대한 고민을 해야하고 그러기에

일단은 날짜만 제대로 표현을 하는게 중요하다고 생각하여 결정했다.

3. DB에 새로 값을 입력시 Today로 자동 리턴하게 설정

DBManager를 가면 이제 새로운 Field가 생겼으므로 에러가 뜨기에

그걸 해소해주도록 하자

새롭게 DB에 값을 입력할때 날짜값을 오늘날짜로 Default를 주려고한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func addDB (textfield : String ) {
        let dateFormat = DateFormatter()
        dateFormat.dateFormat = "yyyy-MM-dd"
        dbModel.db.collection(Constants.collectionName).addDocument(data: [Constants.Fire.fireId : self.getID()
                                                                           , Constants.Fire.fireTitle : textfield
                                                                           , Constants.Fire.fireBool : false, Constants.Fire.favBool : false
                                                                           , Constants.Fire.fireImageTitle : ""
                                                                           , Constants.Fire.fireDate : dateFormat.string(from: Date()) // new
                                                                           ]) { (error) in
            if let e = error { // DB에 업로드중 에러 발생시
                print("error : \(e.localizedDescription)")
            } else { // 업로드가 성공하면 콘솔로 알려준다.
                print("Upload Done")
            }
        }
    }

DB를 가져오는 부분에 date를 추가해주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func getData () {
        
        dbModel.db.collection(Constants.collectionName).order(by: Constants.Fire.fireId)
            .addSnapshotListener { (querySnapshot, error) in
                
                self.dbModel.lists = []
                
                if let e = error {
                    print("error : \(e)")
                } else {
                    if let snapshotDocuments = querySnapshot?.documents {
                        for doc in snapshotDocuments {
                            let data = doc.data()
                            if let listId = data[Constants.Fire.fireId] as? Int
                                , let listTitle = data[Constants.Fire.fireTitle] as? String
                                , let listBool = data[Constants.Fire.fireBool] as? Bool
                                , let listFav = data[Constants.Fire.favBool] as? Bool
                                , let listImage = data[Constants.Fire.fireImageTitle] as? String,
                               let listDate = data[Constants.Fire.fireDate] as? String // new
                               {
                                
                                let list = ToDoModel(id: listId, title: listTitle, isComplete: listBool, isFav: listFav, imageTitle: listImage, date: listDate)
                                self.dbModel.lists.append(list)
                                self.delegate?.sendDB(data: self.dbModel.lists)
                            }
                            
                        }
                    }
                }
            }
    }

테스트를 해보자

장보기라고 만들었다.

DB를 확인해보니, 오늘 날짜가 제대로 입력된걸 알 수있다.

4. 날짜를 선택한값을 DB로 보내기.

1
2
3
4
5
6
7
8
9
10
11
@IBAction func setDate(_ sender: UIDatePicker) {
        
        let datePicker = sender
        let dateFormat = DateFormatter()
        dateFormat.dateFormat = "yyyy-MM-dd"
        
        // new
        let selectedDate = dateFormat.string(from: datePicker.date) 
        dbManager.addCellDetailFeature(title: titleLabel.text!, field: Constants.Fire.fireDate, feature: selectedDate)
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func addCellDetailFeature (title: String, field : String ,feature : String) {
        dbModel.db.collection(Constants.collectionName).whereField(Constants.Fire.fireTitle, isEqualTo: title).getDocuments { (querySnapshot,error) in
            if let e = error {
                print(e)
            } else {
                if let documents = querySnapshot?.documents {
                    for doc in documents {
                        let docuId = doc.documentID
                        self.dbModel.db.collection(Constants.collectionName).document(docuId).setData([field : feature], merge: true)
                    }
                }
            }
        }
    }

기존에 이미지만 추가하던것을 파라미터를 좀더 확장하는게 좋다고 판단하여 내용을 바꿔주었다.

29일로 바꿔보았다.

잘 바뀐것을 알 수 있다.

3. 오늘 버튼 눌렀을때 오늘 해당하는 list 보여주기.

우선 CellDetailVC에 새로운 변수를 하나 만들어준다.

var selectedDate : String? = ""

그리고 셀을 클릭했을때 date값을 같이 넘기기 위해 TableViewVC에 다음과 같이 적어서 화면전환시 같이 넘기게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
extension TableViewController : UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        if let CellDetailVC = self.storyboard?.instantiateViewController(identifier: "CellDetailViewController") as? CellDetailViewController {
            
            CellDetailVC.titleString = lists[indexPath.row].title
            CellDetailVC.selectedDate = lists[indexPath.row].date 
            // new
            
            if lists[indexPath.row].imageTitle != "" { // 이미지 파일을 업로드 한 경우
                CellDetailVC.imageUrl = lists[indexPath.row].imageTitle
            } else { // 이미지 파일을 업로드 하지 않은 경우
                CellDetailVC.imageUrl = "https://firebasestorage.googleapis.com/v0/b/todolist-1a790.appspot.com/o/upload-image-icon.png?alt=media&token=52da5077-bebf-4f39-8692-14b376f6f7a6"
            }
            
            self.present(CellDetailVC, animated: true)
        }
        
        
    }
    
}

화면 전환시 보이게 해줘야하므로 ViewDidload부분을 수정하도록 하자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
override func viewDidLoad() {
        super.viewDidLoad()
        
        let dateFormat = DateFormatter() //new
        dateFormat.dateFormat = "yyyy-MM-dd" //new
        
        let date = dateFormat.date (from:selectedDate) // new
        
        datePicker.setDate(date!, animated: false) //new
        titleLabel.text = titleString
        
        
        getImage()
    }

현재 화면전환시 받아오는 파라미터의 타입이 optionalString이므로

해당 값은 nil이 되지 않기에 강제로 Force Unwrapping 해주었다.

그리고 String → Date로 형변환을 해줘야 하기에 dateFormat 을 사용 하였다.

테스트를 해보면?

잘된다.

이미지는 용량상 어쩔수없이 화질저화를 했다 ㅠ 근데 사이즈도 너무 작아져버렸네..

Nas통해 호스팅하는걸 빨리 해야하지 않을까싶다.

이제 Date이용해서 하나의 기능만 더 구현하면 끝이 날것같다.

그런데 마지막기능이 화룡점정 느낌인 만큼 헬스멜이 난다.

그래도 아직 시간적 여유가 있으니 도전해보자.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.