Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Damagucci-Juice] step3 델리게이트 프로토콜로 작동하던 것을 노티피케이션 센터를 이용해서 설정 #90

Open
wants to merge 18 commits into
base: gucci
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
cb53995
Fix : changeColorAndAlpha(_ rectangle: ) 에서 딕셔너리 value 값을 remove로 추출
Damagucci-Juice Mar 16, 2022
c371611
Add : NotificationCenter.swift 를 추가하고 기초 설정 셋업
Damagucci-Juice Mar 16, 2022
ceff310
Fix : addRectangle 을 delegate에서 notificationCenter로 이관하려는 사전작업
Damagucci-Juice Mar 16, 2022
d27d4b1
Feat : Plane class 에 addRectangle() 에서 post를 날림
Damagucci-Juice Mar 16, 2022
3641efe
Feat : ViewController 에서 @objc made(rectangleNoti:) 를 완성
Damagucci-Juice Mar 16, 2022
dd2dfe7
Feat : notification observer 해체 조건 설정
Damagucci-Juice Mar 16, 2022
b20630d
Feat : addOberserver(... obeject: nil) 로 설정
Damagucci-Juice Mar 16, 2022
6ad2131
Chore : 사용하지 않는 주석제거
Damagucci-Juice Mar 16, 2022
e608915
Fix : 사각형 뷰가 터치가 되면 NotificationCenter를 이용하도록 변경
Damagucci-Juice Mar 16, 2022
5c84c26
Fix : 사각형 뷰의 알파값이 변경되면 NotificationCenter를 이용하도록 변경
Damagucci-Juice Mar 16, 2022
1ae7c3f
Chore : 줄 사이 불필요한 띄어쓰기 제거
Damagucci-Juice Mar 16, 2022
31dccbe
Fix : 터치를 가능하게 하는 함수명 변경
Damagucci-Juice Mar 16, 2022
04ae57a
Update : README.md 수정
Damagucci-Juice Mar 16, 2022
9499d67
Refactor : NotificationCenter.Name 과 NotificationKey 의 이름 변경
Damagucci-Juice Mar 18, 2022
b252af7
Refactor : Notification post 와 addObserver 에서 object 를 nil 에서 self, p…
Damagucci-Juice Mar 18, 2022
da788e9
Refactor : 뷰 생명주기 중 viewWillAppear() 에 NotificationCenter 를 다루는 함수를 옮김
Damagucci-Juice Mar 18, 2022
57a67ca
Refactor : showDetailOfColorAndAlpha() 에 rectangle 매개변수를 넘김
Damagucci-Juice Mar 18, 2022
7daa8fe
Refactor : @objc ... Notification 에 관련된 함수들에서 노티피케이션의 이름을 변경
Damagucci-Juice Mar 18, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions DrawingApp/DrawingApp/NotificationCenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// NotificationCenter.swift
// DrawingApp
//
// Created by YEONGJIN JANG on 2022/03/16.
//

import Foundation

let notificationCenter = NotificationCenter.default
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationCenter.default를 이렇게 줄이는 게 썩 좋은 표현이나 접근이 아닙니다.
싱글톤으로 접근하는 NotificationCenter.default 이런 값을 참조해 놓는 것이 좋지 않은 경우도 있습니다. 줄여쓰는 효과가 있는게 아니면 그대로 써도 됩니다.
NotificationCenter는 덜하지만 default가 참조하는 포인터가 바뀌는 경우도 있습니다. 그래서 이렇게 담아놓으면 다른 인스턴스를 가르킬 수도 있습니다. 싱글톤을 쓸 때는 그냥 이걸 쓰는게 더 좋습니다.


extension Notification.Name {
static let addRectangleView = Notification.Name("A rectangle is made")
static let tappedRectangleView = Notification.Name("a rectangle view is Tapped")
static let colorChange = Notification.Name("color changed")
static let alphaChange = Notification.Name("alpha changed")
}

enum NotificationKey {
case color
case alpha
case tappedRectangle
case addedRectangle
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

노티는 행위에 가깝고, 키는 변수에 가까우니 그런 형태면 좋을 것 같고. 그외에는 겹치지 않는지. 명확한 의도를 전달하는 지 살펴보면 될 것 같습니다.
그리고 상수는 항상 관련있는 타입이 있습니다. 사용하는 타입도 있고, 전송하는 타입도 있죠. 어떤 타입과 관련이 높은지 살펴보고 응집도 높게 합쳐보세요.

}
30 changes: 4 additions & 26 deletions DrawingApp/DrawingApp/Plane.swift
Original file line number Diff line number Diff line change
@@ -1,27 +1,7 @@
import Foundation

protocol RectangleAddedDelegate {
func made(rectangle : Rectangle)
}

protocol RectangleTouchedDelegate {
func touched(rectangle: Rectangle)
}

protocol RectangleColorChangeDelegate {
func didChangeColor(rectangle: Rectangle)
}
protocol RectangleAlaphaChangeDelegate {
func didChangeAlpha(rectangle: Rectangle)
}

class Plane {
private var rectangles: [Rectangle] = []

var addedRectangleDelegate :RectangleAddedDelegate?
var rectangleTapDelegate : RectangleTouchedDelegate?
var colorDelegate: RectangleColorChangeDelegate?
var alphaDelegate: RectangleAlaphaChangeDelegate?

var rectangleCount: Int {
return rectangles.count
Expand All @@ -31,15 +11,13 @@ class Plane {
func addRectangle() {
let rectangle: Rectangle = Factory.createRandomRectangle()
rectangles.append(rectangle)

addedRectangleDelegate?.made(rectangle: rectangle)
notificationCenter.post(name: .addRectangleView, object: nil, userInfo: [NotificationKey.addedRectangle : rectangle])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationCenter의 object는 sender의 의미라서 보내는 객체도 항상 지정해주는 게 좋습니다. 그래야 옵저버 조건에서 매칭이 가능합니다

}

subscript(index: Int) -> Rectangle {
return rectangles[index]
}

//MARK: Bound Gate
private func isTouchedOnRectangle(at point: Point) -> Rectangle? {
var optionalRectangle: Rectangle?
for rectangle in rectangles {
Expand All @@ -54,18 +32,18 @@ class Plane {
guard let rectangle = isTouchedOnRectangle(at: point) else {
return
}
self.rectangleTapDelegate?.touched(rectangle: rectangle)
notificationCenter.post(name: Notification.Name.tappedRectangleView, object: nil, userInfo: [NotificationKey.tappedRectangle: rectangle])
}

func changeColorOfRectangle(_ rectangle: Rectangle) {
var oldRectangle = rectangle
let newRectangle = oldRectangle.changeColor()
self.colorDelegate?.didChangeColor(rectangle: newRectangle)
notificationCenter.post(name: Notification.Name.colorChange, object: nil, userInfo: [NotificationKey.color: newRectangle])
}

func changeAlpha(_ rectangle: Rectangle, _ alpha: Int) {
var willChangeRectangle = rectangle
willChangeRectangle = willChangeRectangle.changeAlpha(alpha)
alphaDelegate?.didChangeAlpha(rectangle: willChangeRectangle)
notificationCenter.post(name: Notification.Name.alphaChange, object: nil, userInfo: [NotificationKey.alpha: willChangeRectangle])
}
}
62 changes: 43 additions & 19 deletions DrawingApp/DrawingApp/ViewController/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,18 @@ class ViewController: UIViewController {
self.plane.addRectangle()
}

private func declareDelegates() {
plane.addedRectangleDelegate = self
plane.rectangleTapDelegate = self
plane.colorDelegate = self
plane.alphaDelegate = self
private func activateNotificationObservers() {
// add rectangle
notificationCenter.addObserver(self, selector: #selector(made(rectangleNoti: )), name: Notification.Name.addRectangleView, object: nil)

// rectangle tapped
notificationCenter.addObserver(self, selector: #selector(touched(rectangleNoti:)), name: Notification.Name.tappedRectangleView, object: nil)

// color changed
notificationCenter.addObserver(self, selector: #selector(didChangeColor(rectangleNoti:)), name: Notification.Name.colorChange, object: nil)

// alpha changed
notificationCenter.addObserver(self, selector: #selector(didChangeAlpha(rectangleNoti: )), name: Notification.Name.alphaChange, object: nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옵저버를 등록할 때도 가능하면 object (sender)를 지정해주는 게 좋습니다. 그래야 그 타입이 보내는 그 메시지를 받을 수 있죠.
물론 모든 타입이 보내는 메시지를 받을 때는 생략 가능합니다

}

private func initDetailView() {
Expand All @@ -58,14 +65,19 @@ class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
declareDelegates()

activateNotificationObservers()
initDetailView()
touchBackgroundView()
activateBackgroundTappable()
}

override func viewDidDisappear(_ animated: Bool) {
notificationCenter.removeObserver(self)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

viewDidLoad는 뷰 컨트롤러 생성되고 한 번만 호출되지만, viewDidDisappear는 다른 뷰로 넘어가도 불리고 여러번 불릴 수 있죠.
만약 이렇게 짝을 지으면 다른 뷰 컨트롤로 화면으로 넘어갔다 돌아오면 그 때부터 노티를 못 받습니다. 그래도 될까요?


viewDidDisappear() 처럼 override 하는 함수는 꼭 super.xxx를 호출해주셔야 합니다. 그렇지 않으면 그 상태가 된 다음에 오동작을 할 수 있습니다.

}
}

extension ViewController: UIGestureRecognizerDelegate {
func touchBackgroundView() {
func activateBackgroundTappable() {
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
self.view.addGestureRecognizer(tap)
}
Expand All @@ -77,16 +89,19 @@ extension ViewController: UIGestureRecognizerDelegate {
}
}

extension ViewController: RectangleAddedDelegate {
func made(rectangle: Rectangle) {
extension ViewController {
@objc func made(rectangleNoti : Notification) {
guard let rectangle = rectangleNoti.userInfo?[NotificationKey.addedRectangle] as? Rectangle else {
return
}
let rectView = UIView(frame: CGRect(x: rectangle.point.x, y: rectangle.point.y, width: rectangle.size.width, height: rectangle.size.height))
rectView.backgroundColor = UIColor(red: CGFloat(rectangle.color.R)/255.0, green: CGFloat(rectangle.color.G)/255.0, blue: CGFloat(rectangle.color.B)/255.0, alpha: CGFloat(rectangle.alpha.rawValue)/10.0)
self.rectangleAndViewContainer[rectangle] = rectView
self.view.addSubview(rectView)
}
}

extension ViewController: RectangleTouchedDelegate {
//
extension ViewController {
private func paintBorder(_ rectangle: Rectangle) {
let touchedView = self.rectangleAndViewContainer[rectangle]
touchedView?.layer.borderWidth = 3.0
Expand All @@ -107,7 +122,11 @@ extension ViewController: RectangleTouchedDelegate {
alphaSlider.value = Float(rect.alpha.rawValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (key, value) in rectangleAndViewContainer 에서 view로 찾는 것은 좀 이상한데요.
이렇게 찾을꺼면 key를 view로 하는 게 맞는 거구요,
showDetailOfColorAndAlpha()를 어디서 호출하는 지 모르겠지만 view로 찾지 말고 rectangle로 view를 찾는 게 적당합니다.

}
}
func touched(rectangle: Rectangle) {

@objc func touched(rectangleNoti: Notification) {
guard let rectangle = rectangleNoti.userInfo?[NotificationKey.tappedRectangle] as? Rectangle else {
return
}
if let view = self.selectedView {
view.layer.borderWidth = .zero
}
Expand All @@ -116,25 +135,30 @@ extension ViewController: RectangleTouchedDelegate {
}
}

extension ViewController: RectangleColorChangeDelegate {
extension ViewController {
func changeColorAndAlpha(_ rectangle: Rectangle) {
let viewValue = rectangleAndViewContainer[rectangle]
rectangleAndViewContainer.removeValue(forKey: rectangle)
let viewValue = rectangleAndViewContainer.removeValue(forKey: rectangle)
rectangleAndViewContainer[rectangle] = viewValue
if let view = rectangleAndViewContainer[rectangle] {
view.backgroundColor = UIColor(red: CGFloat(rectangle.color.R)/255.0, green: CGFloat(rectangle.color.G)/255.0, blue: CGFloat(rectangle.color.B)/255.0, alpha: CGFloat(rectangle.alpha.rawValue)/10.0)
self.rectangleAndViewContainer.updateValue(view, forKey: rectangle)
}
}

func didChangeColor(rectangle: Rectangle) {
@objc func didChangeColor(rectangleNoti: Notification) {
guard let rectangle = rectangleNoti.userInfo?[NotificationKey.color] as? Rectangle else {
return
}
changeColorAndAlpha(rectangle)
colorButton.setTitle("#\(rectangle.color.showRGBVlaue())", for: .normal)
}
}

extension ViewController : RectangleAlaphaChangeDelegate {
func didChangeAlpha(rectangle: Rectangle) {
extension ViewController {
@objc func didChangeAlpha(rectangleNoti: Notification) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rectangleNoti 는 notification의 이름으로 부정확한 것 같습니다.
사각형태노티(그런게 있는지 모르겠지만)인지, 사각형에 대한 노티인지, 사각형 변화에 대한 노티인지 모르겠습니다.
그럴수록 단어를 억지로 줄여쓰지 않는게 좋습니다.

Copy link
Author

@Damagucci-Juice Damagucci-Juice Mar 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@objc func didChangeAlpha(from plane: Notification) {

플레인으로 부터 온 노티피케이션이라고 표현을 해보았는데 괜찮을까요?

guard let rectangle = rectangleNoti.userInfo?[NotificationKey.alpha] as? Rectangle else {
return
}
changeColorAndAlpha(rectangle)
alphaSlider.value = Float(rectangle.alpha.rawValue)
}
Expand Down
165 changes: 163 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# swift-drawingapp
3번째 미션 - iOS 터치 드로잉 앱

## step-1 주요 작업 내용
## step1 주요 작업 내용
- [x] Device - iPad, Device Orientation - Landscape Left, Landscape Right)
- [x] Factory 클래스에서 랜덤하게 Rectangle 클래스 생성

Expand Down Expand Up @@ -36,4 +36,165 @@ struct BackgroundColor {
}
```

![os_log](https://user-images.githubusercontent.com/50472122/156562405-f07d43dc-e2a6-4b82-81de-cb4ac1381613.png)
![os_log](https://user-images.githubusercontent.com/50472122/156562405-f07d43dc-e2a6-4b82-81de-cb4ac1381613.png)

## step2 주요 작업 내용

- [x] MVC 중에서 M -> C 로 갈 때 delegate 패턴을 적용
- [x] "사각형" 버튼을 누르면 사각형 View 추가
- [x] Slider 의 변화에 따라 view 투명도 조절
- [x] 버튼 누르면 사각형 뷰의 색이 변경
- [x] 색 버튼의 title 변경
- [x] 선택된 사각형 뷰에 선택됨을 알리표는 테두리 추가
- [x] 화면에 다른 사각형을 선택시 테두리 걷어가기

## 주요 구현 모습

### 사각형 버튼을 누르면 사각형 UIView 를 추가

* ViewController.swift

```
@IBAction func rectangleButtonTouched(_ sender: Any) {
self.plane.addRectangle()
}

override func viewDidLoad() {
super.viewDidLoad()
plane.addedRectangleDelegate = self
}

```

* Plane.swift

```
var addedRectangleDelegate :RectangleAddedDelegate?

func addRectangle() {
let rectangle: Rectangle = Factory.createRandomRectangle()
rectangles.append(rectangle)

addedRectangleDelegate?.made(rectangle: rectangle)
}

...

protocol RectangleAddedDelegate {
func made(rectangle : Rectangle)
}

```

* extension : ViewController

```

extension ViewController: RectangleAddedDelegate {
func made(rectangle: Rectangle) {
let rectView = UIView(frame: CGRect(x: rectangle.point.x, y: rectangle.point.y, width: rectangle.size.width, height: rectangle.size.height))
rectView.backgroundColor = UIColor(red: CGFloat(rectangle.color.R)/255.0, green: CGFloat(rectangle.color.G)/255.0, blue: CGFloat(rectangle.color.B)/255.0, alpha: CGFloat(rectangle.alpha.rawValue)/10.0)
self.rectangleAndViewContainer[rectangle] = rectView
self.view.addSubview(rectView)
}
}

```

### 배경에 tap이 될 수 있도록 적용

> ViewController 에 UIGestureRecognizerDelegate 를 적용하고 그 함수를 채택해야함

* ViewController.swift

```
override func viewDidLoad() {
super.viewDidLoad()
activateBackgroundTappable()
}

extension ViewController: UIGestureRecognizerDelegate {
func activateBackgroundTappable() {
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
self.view.addGestureRecognizer(tap)
}

@objc func handleTap(_ sender: UITapGestureRecognizer) {
let viewPoint: CGPoint = sender.location(in: self.view)
let rectPoint: Point = Point(x: viewPoint.x, y: viewPoint.y)
plane.touchedRectangle(at: rectPoint)
}
}

```

## step3 주요 구현내용

- [x] Delegate로 작동하는 것을 NotificationCenter 를 이용하도록 설정
- [x] 사각형 추가 기능
- [x] rectangle view 를 터치하면 테두리 설정
- [x] rectangle color 변경
- [x] rectangle alpha 변경

### 사각형 추가 기능을 NotificationCenter 를 이용해서 설정

* NotificationCenter.swift

```
let notificationCenter = NotificationCenter.default

extension Notification.Name {
static let addRectangleView = Notification.Name("A rectangle is made")
static let tappedRectangleView = Notification.Name("a rectangle view is Tapped")
static let colorChange = Notification.Name("color changed")
static let alphaChange = Notification.Name("alpha changed")
}

enum NotificationKey {
case color
case alpha
case tappedRectangle
case addedRectangle
}

```

* Plane.swift

```
func addRectangle() {
let rectangle: Rectangle = Factory.createRandomRectangle()
rectangles.append(rectangle)
// Notification.post
notificationCenter.post(name: .addRectangleView, object: nil, userInfo: [NotificationKey.addedRectangle : rectangle])
}
```

* ViewController.swift

```
override func viewDidLoad() {
super.viewDidLoad()
notificationCenter.addObserver(self, selector: #selector(made(rectangleNoti: )), name: Notification.Name.addRectangleView, object: nil)
}

override func viewDidDisappear(_ animated: Bool) {
notificationCenter.removeObserver(self)
}

@objc func made(rectangleNoti : Notification) {
guard let rectangle = rectangleNoti.userInfo?[NotificationKey.addedRectangle] as? Rectangle else {
return
}
let rectView = UIView(frame: CGRect(x: rectangle.point.x, y: rectangle.point.y, width: rectangle.size.width, height: rectangle.size.height))
rectView.backgroundColor = UIColor(red: CGFloat(rectangle.color.R)/255.0, green: CGFloat(rectangle.color.G)/255.0, blue: CGFloat(rectangle.color.B)/255.0, alpha: CGFloat(rectangle.alpha.rawValue)/10.0)
self.rectangleAndViewContainer[rectangle] = rectView
self.view.addSubview(rectView)
}

```

## 구현화면

![drawing-step3](https://user-images.githubusercontent.com/50472122/158532985-1b8aea8d-307d-42f8-81f6-5adad2744bf2.gif)