UIApplication : 시스템으로부터 이벤트를 받아 Custom Objects로 이벤트를 전달하고 상위 레이어의 앱 동작을 조정
App Delegate : App의 초기화, 상태변화, 다른 앱의 기타 이벤트를 핸들링
View Controller : App의 Contents가 화면에 보이게 하는 역할
UIWindow : 화면에 보여지는 뷰 객체들을 조정
View & Control & layer : Contents의 시각적인 표현을 담당
application(_:didFinishLaunchingWithOptions:)
메시지 전송메인 런 루프 : 앱에 들어오는 이벤트들을 애플리케이션 객체가 처리할 수 있도록 계속해서 라우팅하고, 처리결과로 UI와 상태를 업데이트
이벤트 처리를 위한 loop로 입력소스와 타이머소스를 주로 처리.
사용자 이벤트 발생 및 처리과정
전달받은 이벤트를 앱의 run loop에서 처리하는 과정
restorationIdentifier
)myViewController.restorationIdentifier = "thiedViewController"
//Controller 내부
//해당 변수 선언
var aVariable : object!
//viewDidLoad() 함수 내부 : delegate할당 및 변수 할당
let appDelegate = UIApplication.shared.delegate as! AppDelegate
aVariable = appDelegate.value
상태변화 : Not running, Active, Inactive, Background, Suspended
not runnig : 실행중이지 않던 프로그램이 실행이 된다
foreground: 앱이 실행 된다
background 홈버튼 눌러서 내리기 (끈거 x. 코드는 실행되고 있음)
suspended 상태 : background로 실행되는 앱은 일정한 이유에 따라 OS가 앱을 끈다. 코드 실행하지 않는 상태로 변경
encodeWithCoder(_:)
와 init(coder:)
를 구현해야 한다.UserDefaults란?
func saveChanges(_ machine : VendingMachine) {
let encodedData = NSKeyedArchiver.archivedData(withRootObject: machine)
userDefaults.set(encodedData, forKey : "vendingMachine")
}
func loadMachine() -> VendingMachine? {
guard let encodedData = userDefaults.data(forKey: "vendingMachine") else { return nil }
guard let archivedMachine = NSKeyedUnarchiver.unarchiveObject(with: encodedData) as? VendingMachine else { return nil }
return archivedMachine
}
싱글톤 패턴이란? 매번 똑같은 하나의 인스턴스만 반환하도록 하는 클래스 설계 방식
클래스 경우 싱글톤을 생성하는 방법
init에 private이 걸려있어 직접 선언과 동시 호출을 제한시킨다.
class VendingMachine {
static var instance: VendingMachine = VendingMachine()
private init(){
}
}
싱글톤 객체의 장점과 단점에 대해 학습한다.
관찰자 패턴이란? 객체 사이에 일 대 다의 의존 관계를 정의 해두어, 어떤 객체의 상태가 변할 때 그 객체에 의존성을 가진 다른 객체들이 그 변화를 통지 받고 자동으로 업데이트될 수 있게 만만드는 것
적용방법 :
ViewController : viewDidLoad() 함수 내 Observer 등록
NotificationCenter.default.addObserver(self, selector: #selector(updateInventoryLabels(notification:)), name: Notification.Name("didUpdateInventory"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(updateBalanceLabel(notification:)), name: Notification.Name("didUpdateBalance"), object: nil)
Model(VendingMachine) : 해당 메서드 내부 Observer post 적용
NotificationCenter.default.post(name: Notification.Name("didUpdateInventory"), object: self, userInfo: [
"inventory" : inventory])
ViewController : 라벨 또는 버튼 관련 메서드 Notification 반영
@objc private func updateInventoryLabels(notification : Notification) {
guard let userInfo = notification.userInfo as? [String : Inventory] else { return }
guard let inventory = userInfo["inventory"] else { return }
updateInventory(inventory)
}
왜 사용하는 것일까? (모델과 컨트롤러가 직접 참조하지 않고 느슨하게 연결된 (loosed coupled) 구조가 왜 좋은 것일까?)
실행화면
실행이후 구매 목록을 화면 아래 이미지로 추가한다. : viewDidLoad() 내부 updateListOfPurchase() 메서드 추가
private func updateListOfPurchase() {
guard let productsSold = vendingMachine?.generateListOfHistory() else { return }
var xOfImage = 70
for oneProduct in productsSold {
let productImg = UIImage(named : getImgSource(ObjectIdentifier(type(of : oneProduct)))) ?? UIImage()
let imageView = UIImageView(image : productImg)
imageView.frame = CGRect(x: xOfImage, y: 650, width: 150, height: 150)
self.view.addSubview(imageView)
xOfImage += 80
}
}
특정 제품을 구매할 때마다 해당 제품 이미지를 추가하도록 구현한다. : Notification 등록
NotificationCenter.default.addObserver(self, selector: #selector(updateListOfPurchase(notification:)), name: .didUpdateListOfPurchase , object: nil)
NotificationCenter.default.post(name: .didUpdateListOfPurchase, object: self)
뷰를 코드로 생성해서 추가하는 것과 스토리보드 상에서 미리 생성하는 것의 차이
실행화면
주요개념
swift view 데이터 통신
dismiss(animated: true, completion: nil)
in B ViewControlleroverride func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let adminViewController = segue.destination as? AdminViewController else { return }
adminViewController.adminVendingMachine = self.vendingMachine as? AdminMode
}
실행화면
setNeedsDisplay()
실행화면
구현방법
touch-status 설정
enum Status {
case none
case began
case moved
case ended
}
private var status : Status = .none {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
switch status {
case .none: drawPieGraph(ctx)
case .began: drawCircle(ctx)
case .moved: drawPieGraph(ctx)
case .ended: drawPieGraph(ctx)
}
super.draw(rect)
}
touch-관련 메서드
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
status = .began
super.touchesBegan(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
status = .moved
guard let touch = touches.first else { return }
let locationTouchedFirst = touch.location(in: self)
let viewCenter = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5)
let xOfDistance = pow(viewCenter.x - locationTouchedFirst.x, 2)
let yOfDistance = pow(viewCenter.y - locationTouchedFirst.y, 2)
let distance = sqrt(xOfDistance + yOfDistance)
graphRatio = Double(distance) / Double(viewCenter.y)
super.touchesMoved(touches, with: event)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
status = .ended
super.touchesEnded(touches, with: event)
}
Responder-chain
참조 : https://medium.com/@audrl1010/event-handling-guide-for-ios-68a1e62c15ff