포스트

단어장 프로젝트 (4)

4일차 시작.

사실 오래걸릴것같았던 기능들이 하루만에 해결이 되어서 뭘 할지 고민이 많다.

추가로 게임기능 하나정도만 더하면 아마 괜찮지 않을까? 싶어서 하나 더 구현해본다.

아마 제일 빡세지 않을까? 라는 생각이 좀 든다.

우선 알파벳 버튼을 만들어야하는데 A to Z 너무 많다.

이것도 LabelFactory처럼, ButtonFactory를 만들어 준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class ButtonFactory {
    
    func makeButton(title: String, color: UIColor = .black, backgroundColor: UIColor = .lightGray, completion: @escaping (UIAction) -> Void) -> UIButton {
        let button = UIButton()
        button.setTitle(title, for: .normal)
        button.setTitleColor(color, for: .normal)
        button.backgroundColor = backgroundColor
        button.layer.borderWidth = 0.3
        button.addAction(UIAction(handler: completion), for: .touchUpInside)
        return button
    }
    
}

이렇게 되면 addaction도 만들면서 직접 설정이 가능.

그리고 UIDesign을 시작.

결과는 다음과 같다.

Simulator Screenshot - iPhone 15 Pro - 2024-05-16 at 17 21 38

이제 Hangman 이미지를 만들어 준다.

7

이런식으로 디자인을 했다.

실패횟수는 7회로 하면 될듯하다.

Word 부분을 UIView로 만들고 안에 단어의 글자 만큼 _ 로 나오게 표현을 해보았다.

1
2
3
4
5
6
7
8
9
10
11
private func makeWordLabel () {
        for i in 0 ... dummyList[gameCount].words.count - 1 {
            print(dummyList[gameCount].words)
            let label = LabelFactory().hangManLabel(title: "_", size: 20, isBold: true)
            hangManBodyView.wordFrameView.addSubview(label)
            
            label.snp.makeConstraints {
                $0.leading.equalTo(hangManBodyView.wordFrameView.snp.leading).offset(i * 20)
            }
        }
    }

그리고 HangMan게임용 Label을 또 만들어주었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func hangManLabel (title: String, color: UIColor = .black, size: CGFloat, tag:Int, textAlignment: NSTextAlignment = .center, isBold: Bool) -> UILabel {
        let label = UILabel()
        label.text = title
        label.textColor = color
        if isBold == true {
            label.font = UIFont.boldSystemFont(ofSize: size)
        } else {
            label.font = UIFont.systemFont(ofSize: size)
        }
        label.tag = tag
        label.textAlignment = textAlignment
        label.numberOfLines = 0
        
        return label
    }

이것을 만든 목적은 바로 저 tag를 사용하는데 있다.

이제 게임과 관련된 로직을 구현해야하는데 순환참조가 발생한다.

1
2
3
4
5
6
7
lazy var buttonA = ButtonFactory().makeButton(title: "A") { [weak currentVC, weak self] _ in
        if currentVC?.dummyList[currentVC!.gameCount].words.contains("A") == true {
            
        } else {
            
        }
    }

바로 여기서 buttonA 자기 자신에 대해서 처리를 해야하다보니. 순환참조 에러가 발생.

고민을 하다가 이 부분을 튜터님께 여쭤보니 Notification Center를 사용을 해보는게 어떠냐고 하신다.

Notification Center 란?

Notification Center에 등록된 Event가 발생하면 해당 Event에 대한 행동을 취한다.

우선 ture / false에 따른 Notification Name을 extension을 통해 만들어 준다.

1
2
3
4
extension Notification.Name {
    static let trueNotification = Notification.Name("trueNotification")
    static let falseNotification = Notification.Name("falseNotification")
}

static을 사용함으로써, 쉽게 사용할수있게 해둠.

그리고 VC로 가서 addObserver를 해준다. (Viewdidload)

1
2
NotificationCenter.default.addObserver(self, selector: #selector(trueAnswer), name: .trueNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(falseAnswer), name: .falseNotification, object: nil)

그리고 버튼에다가는

1
2
3
4
5
6
7
8
9
lazy var buttonA = ButtonFactory().makeButton(title: "A") { [weak currentVC] buttonA in
        if currentVC?.dummyList[currentVC!.gameCount].words.contains("A") == true {
            print("true")
            NotificationCenter.default.post(name: .trueNotification, object: buttonA)
        } else {
            print("false")
            NotificationCenter.default.post(name: .falseNotification, object: buttonA)
        }
    }

이렇게 하다가 도저히 @objc에서 안되어서 결국 또 Combine에 이어 GPT에게 도움을 요청 근본적으로 button을 만들때 기존처럼 button.addAction을 통해서 바로 접근하던게 떠올랐는데 그걸 전부 다 길게 할 수 없어서 factory처럼 가능한지 물어봤다.

1
2
3
4
5
6
7
8
9
10
11
12
func makeButton(title: String, color: UIColor = .black, backgroundColor: UIColor = .lightGray, completion: @escaping (UIButton) -> Void) -> UIButton {
    let button = UIButton()
    button.setTitle(title, for: .normal)
    button.setTitleColor(color, for: .normal)
    button.backgroundColor = backgroundColor
    button.layer.borderWidth = 0.3
    button.addAction(UIAction { action in
        guard let button = action.sender as? UIButton else { return }
        completion(button)
    }, for: .touchUpInside)
    return button
}

이녀석은 이렇게 제시를 했다.

코드를 보니 일리가 있다.

애초에 내가 처음에 만들었던 buttonFactory에서 completion을 저 버튼으로 넘기면 되었는데, 생각이 너무 짧았다.

그냥 UIAction을 리턴시켜야한다는 그 고정관념에 빠져있었다.

Combine에 이어 이번에 이렇게 button 그자체를 넘기는것도 GPT에게 배워간다.

위의 코드는 꼭 잊지않도록 해야겠다.

1
2
3
4
5
6
7
8
lazy var buttonA = ButtonFactory().makeButton(title: "A") { [weak currentVC, weak self] button in
        if currentVC?.dummyList[currentVC!.gameCount].words.contains("A") == true {
            self?.checkWord(button: button, backgroundColor: .blue, systemName: "checkmark")
        } else {
            self?.checkWord(button: button, backgroundColor: .red, systemName: "xmark")
            currentVC?.failCount += 1
        }
    }

이렇게 해서 체크를 했는데

다른 알파벳들도 모두 빨간색으로 리턴한다

확인해보니 currentVC?.dummyList[currentVC!.gameCount].words가 애초에 없다.

전역변수로 첨에 vc를 만든게 문제로 판단.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func checkWord(button: UIButton) {
        guard let currentVC = currentViewController as? HangManGameViewController else { return }
        
        if currentVC.dummyList[currentVC.gameCount].words.contains(button.currentTitle!.lowercased()) {
            button.isEnabled = false
            button.setTitle("", for: .normal)
            button.backgroundColor = .blue
            button.setImage(UIImage(systemName: "checkmark"), for: .normal)
        } else {
            button.isEnabled = false
            button.setTitle("", for: .normal)
            button.backgroundColor = .red
            button.setImage(UIImage(systemName: "xmark"), for: .normal)
            currentVC.failCount += 1
            currentVC.updateMan()
        }
    }

단어문제처럼 이렇게 함수에서 모든걸 처리하게 바꿨다.

버튼도 위와같이 바꿔주었다.

1
2
3
 lazy var buttonA = ButtonFactory().makeButton(title: "A") { [weak self] button in
        self?.checkWord(button: button)
    }

그리고 VC로 돌아가서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private func gameStart () {
        dummyList = dummyGenerator.makeDummy()
        makeWordLabel()
        
        updateMan()
        hangManBottomView.isHidden = false
    }
    
func updateMan () {
        if failCount >= 7 {
            hangManBodyView.hangManImageView.image = UIImage(named: imageList[failCount])
            let alert = alertController.makeAlertWithCompletion(title: "게임종료", message: "게임이 끝났습니다.\n다시 시작하시겠습니까?") { [weak self] _ in
                self?.failCount = 0
                self?.gameStart()
            }
            hangManBottomView.isHidden = true
            self.present(alert, animated: true)
        } else {
            hangManBodyView.hangManImageView.image = UIImage(named: imageList[failCount])
        }
    }

이렇게 코드를 작성.

어차피 영단어는 소문자로 입력이 될거같아서, 소문자만 받게처리했는데, 이건 나중에 상황봐서 고치면 될듯하다.

May-17-2024 01-14-53

우선은 구색만 갖춘다.

Notification을 했어도 되었을듯…. Notification도 buttonFactory의 내용을 바꾼 시점에서는 가능했을듯 하다.

다음 추가 기능때 NotificationCenter를 사용해보는걸로…

현재 다시시작을 하게되면 저 버튼이 그대로 남아있어서 저걸 돌리는 방법은 내일 찾아봐야할듯…

그리고 정답일때 표시가 되게도 해야한다.

할게많다.

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