포스트

킥보드 프로젝트 6일차

관리자 페이지 구현.

사실 이번 프로젝트에서 킥보드 등록 기능이 있는데 지금까지 구현한걸로 보았을때,

너무 유져입장에서 만든 것 같아, 관리자 입장에서도 만들어 본다.

우선 admin 계정일때만 새로 만든 Manage tabbar가 나오게 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import UIKit

class TabbarViewController: UITabBarController {
    
    var myID = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.selectedIndex = 0
        self.navigationController?.isNavigationBarHidden = true
        
        self.tabBar.backgroundColor = .white // new
        
        if myID != "admin" { // new
            self.viewControllers?.remove(at: 3)
        }
    }

}

3번째 인덱스(Tabbar도 0부터 시작)를 삭제 하면 아이디가 admin이 아닌경우엔 킥보드 관리를 할 수 없다.

킥보드 등록, 삭제 기능 구현

새로운 VC를 하나 만들어주었다. 그리고 여기서는 간단하게 TableView로 작업을 하려고한다.

그래서 CustomCell을 만들어주었다. 해당 부분은 pass

1. 핀 모델링 및 싱글턴 패턴 사용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct PinModel {

    var x: Double
    var y: Double
    var id: String

}

class SavedPinSingleton {
    
    static let shared = SavedPinSingleton()
    
    var array: [PinModel] = [PinModel]()

    private init () {}
    
}

좌표값과 시리얼 번호만 있으면 될것같아서 구현을 해두었다.

2. TableView 관련 기능 설정.

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
extension ScooterManageViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        SavedPinSingleton.shared.array.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "manageCell", for: indexPath) as? ScooterManageTableViewCell else {
            return UITableViewCell() }
        
        cell.serialLabel.text = "\(indexPath.row + 1)호기 Serial Number: \(SavedPinSingleton.shared.array[indexPath.row].id ?? "1A2B3C4D5E")"
        cell.delteBtn.tag = indexPath.row
        cell.delteBtn.addTarget(self, action: #selector(deleteEvent), for: .touchUpInside)
        cell.selectionStyle = .none
        
        return cell
    }
    
    @objc func deleteEvent(sender: UIButton) {
        
        let alert = UIAlertController(title: "삭제하시겠습니까?", message: "삭제를 하시면 등록된 킥보드 정보가 삭제가 됩니다.\n해당 정보는 되돌릴 수 없습니다.", preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "취소", style: .default))
        alert.addAction(UIAlertAction(title: "확인", style: .destructive, handler: { _ in
            SavedPinSingleton.shared.array.remove(at: sender.tag)
            self.tableView.reloadData()
        }))
        
        self.present(alert, animated: true)
    }
    
}

우선 테이블뷰에는 CustomCell에 관련된걸 넣어주었고. 삭제 버튼에 대한 Event를 selector를 사용하여 처리를 해주었다.

테이블 뷰는 하도 많이 써서그런가 별 감흥이 없다.

3. 기기 추가 기능 구현

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
@IBAction func addPinData(_ sender: UIButton) {
        
        let alert = UIAlertController(title: "킥보드를 추가합니다.", message: "아래에 정보를 입력해주세요.", preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "10자리의 Code를 입력해주세요"
        }
        alert.addTextField { textField in
            textField.placeholder = "경도: ex) -122.030189"
        }
        alert.addTextField { textField in
            textField.placeholder = "위도: ex) 37.331676"
        }
        
        alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in

            if let serial = alert.textFields?[0].text, let lon = Double((alert.textFields?[1].text)!), let lat = Double((alert.textFields?[2].text)!) {
                if serial.count != 10 {
                    let alert = UIAlertController(title: "에러 발생", message: "Serial Number는 반드시 10자리로 입력해 주세요.", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "확인", style: .default))
                    self.present(alert, animated: true)
                } else {
                    SavedPinSingleton.shared.array.append(newItem)
                    
                    self.tableView.reloadData()
                }
            } else {
                let alert = UIAlertController(title: "에러 발생", message: "Field에 값을 입력해 주세요.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "확인", style: .default))
                self.present(alert, animated: true)
            }

        }))
        alert.addAction(UIAlertAction(title: "취소", style: .cancel))
        self.present(alert, animated: true)
    }

simulator_screenshot_08453216-C242-4A7E-9E8D-565EE3C34756

이렇게 배열에 값을 추가하였다.

킥보드 데이터 Coredata로 이관.

지금은 앱을 켤때마다 계속 다르게 생성이 된다. 배열이기때문.

이젠 CoreData를 사용할때가 된거 같아 이부분을 구현한다.

1. 컨테이너 생성

우선 컨테이너를 만들어 준다

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
// AppDelegate
lazy var persistentContainer: NSPersistentContainer = {

    let container = NSPersistentContainer(name: "PinModel")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {

                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

func saveContext () {
    let context = persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {

                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

Appdelegate에 복붙만 하면되기에 패스 물론 이름은 설정해줘야한다.

2. Entity, Attribute 설정

CleanShot 2024-04-29 at 04 56 30@2x

설명은 사진으로 대체

기존 PinModel을 옮긴것이라고 보면 된다.

3. CoordGenerator 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    func makingDummyArray() {
        
        for _ in 0 ... 9 {
            let newItem = PinData(context: context)
            newItem.id = serialGenerator()
            newItem.x = lonlatGenerator().0
            newItem.y = lonlatGenerator().1
            do {
                try context.save()
            } catch {
            }
        }
    
    }

기존에 배열에 추가하고 리턴하던 부분을 수정한다.

배열저장 → Coredata로 저장만 바뀜.

4. MapVC 관련 내용 수정

무분별한 생성을 막기위해서, 다 지웠을때만 생성하게 해두려고한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func makingDummy() {
        for i in SavedPinSingleton.shared.array.indices {
            let coordinate = CLLocationCoordinate2D(latitude: SavedPinSingleton.shared.array[i].y, longitude: SavedPinSingleton.shared.array[i].x)
            let serial = SavedPinSingleton.shared.array[i].id ?? "1A2B3C4D5E"
            addMark(coordinate: coordinate, serial: serial)
        }
    }
    
func getDummy() {
        do {
            SavedPinSingleton.shared.array = try context.fetch(request)
        } catch {
            let alert = UIAlertController(title: "에러 발생", message: "데이터를 로드 하던 중 오류가 발생했습니다.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "확인", style: .default))
            self.present(alert, animated: true)
        }
    }


func refreshPinData () {
        mapView.removeAnnotations(mapView.annotations)
        makingDummy()
    }

싱글턴 배턴을 사용한 배열에 coredata의 값을 불러오는 함수, 그리고 그걸 바탕으로 pin을 지도에 표시하게 하는 함수를 구현해두었다.

그리고 혹시 몰라서 핀을 재생성 하게 하기 위해 refresh기능도 구현해 주었다.

5. ManageVC 수정

여기는 그냥 핀만 추가해주면 되기에,

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
@IBAction func addPinData(_ sender: UIButton) {
        
        let alert = UIAlertController(title: "킥보드를 추가합니다.", message: "아래에 정보를 입력해주세요.", preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "10자리의 Code를 입력해주세요"
        }
        alert.addTextField { textField in
            textField.placeholder = "경도: ex) -122.030189"
        }
        alert.addTextField { textField in
            textField.placeholder = "위도: ex) 37.331676"
        }
        
        alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
            
            
            if let serial = alert.textFields?[0].text, let lon = Double((alert.textFields?[1].text)!), let lat = Double((alert.textFields?[2].text)!) {
                if serial.count != 10 {
                    let alert = UIAlertController(title: "에러 발생", message: "Serial Number는 반드시 10자리로 입력해 주세요.", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "확인", style: .default))
                    self.present(alert, animated: true)
                } else {
                    let newItem = PinData(context: self.context) // new
                    newItem.id = serial // new
                    newItem.x = lon // new
                    newItem.y = lat // new
                    SavedPinSingleton.shared.array.append(newItem)
                    
                    // new
                    do {
                        try self.context.save()
                    } catch {
                        let alert = UIAlertController(title: "에러 발생", message: "데이터 추가 중 오류가 발생했습니다.", preferredStyle: .alert)
                        alert.addAction(UIAlertAction(title: "확인", style: .default))
                        self.present(alert, animated: true)
                    }
                    
                    self.tableView.reloadData()
                }
            } else {
                let alert = UIAlertController(title: "에러 발생", message: "Field에 값을 입력해 주세요.", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "확인", style: .default))
                self.present(alert, animated: true)
            }
            
            
            
        }))
        alert.addAction(UIAlertAction(title: "취소", style: .cancel))
        self.present(alert, animated: true)
    }

핀 추가쪽만 설정해 주었다.

완료사진은 패스.

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