[ios-TIL] schoolfood

감자맨·2022년 8월 21일
0

ios

목록 보기
5/6
post-thumbnail

이번 과제로 schoolfood 주문 화면 만들기였다.
하고나서 보니 stepper로 수량 늘리기, alert띄우기, textfiled로 입력한 값이 충전금액으로 넘어가는것은 잘 구현이 되었는데 충전금액이 저장되어 계속 더해지는게 아니라 초기화가 됐다!
lable값을 엉망으로 줘서 총 충전금액과 결제 금액의 값이 오류가 나네?
lable이 왜 틀렸는지 다시 보았더니 lable을 스토리보드에 배치하고 그 값은 코드로 구성했어야했는데, 그냥 바로 이름만 바꿔서 넣었다!
그러니 당연히 충전금액을 표시할때 조건문이 실행이 안될수밖에;
uilable에 int값 바로 대입한 멍청이..🥺 다른 사람들 코드랑 스승님 코드 보니까 내가 지금까지 수업을 제대로 이해를 못했구나..

올려주신 영상과 코드를 보면서 똑같이 따라해보고 구현 방법에 대해 다시 공부해보고 중요하다 싶은 부분들을 정리해서 올려본다.

//OrderViewController.swift 
@IBOutlet weak var orderTableView: UITableView!
    @IBOutlet weak var walletLabel: UILabel!
    @IBOutlet weak var totalPriceLabel: UILabel!
    
    var totalPrice = 0 {
        willSet { totalPriceLabel.text = "\(newValue.toDecimalFormat())원" }
    }
    
    var wallet = 0 {
        willSet { walletLabel.text = "\(newValue.toDecimalFormat())원"}
    }
    
    var balance: Int {
        return wallet - totalPrice
    }
    
    var quantities = [0, 0, 0, 0]
    

위 코드 중 먼저 var totalPrice!

//Int+Extension.swift
import Foundation

extension Int {
    
    func toDecimalFormat() -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        guard let price = formatter.string(for: self) 
        else {
            return "Formatting Error"
        }
        return price
    }
}

위의 코드는 Int+Extension.swift파일에 있는 코드이다. extension에 Int는 self로 들어가게 된다. 그래서 number가 아닌 경우 실패할때 "Formatting Error"를 나타내준다. 맞게 나오면 price로 값이 출력된다.

위 코드를 만약 viewcontroller에 넣었다면,

//Int+Extension.swift
func toDecimalFormat(Int) -> String {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        guard let price = formatter.string(for: number) 
        else {
            return "Formatting Error"
        }
        return price
    }

위와 같이 고쳐주고 아까 var totalPrice를 고쳐주면 된다.


    var totalPrice = 0 {
        willSet { totalPriceLabel.text = "\(toDecimalFormat(number: newvalue))원" }
    }

이렇게 해주면 이 함수를 int에 다 사용할수 있게 된다.

var balance: Int {
       return wallet - totalPrice
   }

이건 총 충전금액 - 총 결제금액 부분

 var quantities = [0, 0, 0, 0]

이건 메뉴가 4개여서 이렇게 값을 넣어준거고 메뉴 개수를 추가해줄때마다 카운트를 할 수 있게 한것! 왜 난 이런 생각을 못하지 🤔
👇 아래의 코드는 내가 제일 어려워했던 부분을 알려주신 코드!

//OrderViewController.swift
@IBAction func didTapPayBarButtonItem(_ sender: UIBarButtonItem) {
        guard totalPrice > 0 else {
            present(UIAlertController.nothingToPay(), animated:  true)
            return
        }
        present(balance >= 0 ? paymentAlertController() : UIAlertController.insuffientMoney(balance: balance), animated: true)
    }
    
    @IBAction func didTapWalletBarButtonItem(_ sender: UIBarButtonItem) {
        present(rechargeAlertController(), animated: true)
    }
    
    @IBAction func didTapResetButton(_ sender: UIButton) {
        resetAll()
    }
    
    func resetAll() { //여기는 위에 설명했던것들
        totalPrice = 0
        wallet = 0
        quantities = [0, 0, 0, 0]
        orderTableView.reloadData()
    }
}
//OrderViewController.swift
 present(balance >= 0 ? paymentAlertController() : UIAlertController.insuffientMoney(balance: balance), animated: true)

위에 balance가 0 보다 크면 paymentAlertController() 을 실행하고, 0보다 작으면 insuffientMoney를 실행한다.

//OrderViewController.swift
extension OrderViewController {
    func paymentAlertController() -> UIAlertController {
        return UIAlertController.payment(totalPrice: totalPrice,
                                         handler: { [weak self] in
            guard let self = self else { return }
            let balance = self.balance
            self.resetAll()
            self.wallet = balance
        })
    }

self.resetAll()을 해주는 이유는 결제가 성공하면 금액 부분을 초기화를 해줘야하기 때문! 또, 만약 위의 balnace에 금액이 남아 있었다고 한다면,
결제를 하고 나서 남은 금액을 기억해야하기 때문에 self.resetall()을 하면 다 0으로 바꾼 후 self.wallet = balance을 해주면 원래 금액을 넣어주게 된다.

UIAlertController+Extension.swift

//UIAlertController+Extension.swift 
import UIKit

extension UIAlertController {
    
    static func nothingToPay() -> UIAlertController {
        let alertController = UIAlertController(title: "상품 없음", message: "먼저 상품을 추가하세요", preferredStyle: .alert)
        let confirmAction = UIAlertAction(title: "확인", style: .default)
        alertController.addAction(confirmAction)
        return alertController
    }
    
    static func insuffientMoney(balance: Int) -> UIAlertController {
        let alertController = UIAlertController(title: "잔액 부족", message: "\(abs(balance).toDecimalFormat())원이 모자랍니다", preferredStyle: .alert)
        let confirmAction = UIAlertAction(title: "확인", style: .default)
        alertController.addAction(confirmAction)
        return alertController
    }
    
    static func payment(totalPrice: Int, handler: @escaping () -> Void) -> UIAlertController {
        let alertController = UIAlertController(title: "결제", message: "총 \(totalPrice.toDecimalFormat())원을 결제 하시겠습니까?", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "취소", style: .cancel)
        let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in handler() }
        alertController.addAction(cancelAction)
        alertController.addAction(confirmAction)
        return alertController
    }
    
    static func recharge(handler: @escaping (Int) -> Void) -> UIAlertController {
        let alertController = UIAlertController(title: "지갑", message: "얼마를 충전할까요?", preferredStyle: .alert)
        alertController.addTextField { textField in
            textField.keyboardType = .numberPad
        }
        let cancelAction = UIAlertAction(title: "취소", style: .cancel)
        let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in
            guard let text = alertController.textFields?.first?.text,
                  let amount = Int(text) else { return }
            handler(amount)
        }
        alertController.addAction(cancelAction)
        alertController.addAction(confirmAction)
        return alertController
    }
}
  • nothingToPay()함수 : 최종 결제금액이 0원일때 실행. UIAlertController를 이용하여 경고창을 뜨게한다.
  • insuffientMoney()함수 : 내 지갑 금액이 최종 결제금액 보다 적을때 실행. 위와 마찬가지로 경고창을 띄어줌.
  • payment()함수 : 내 지갑 금액이 최종 결제금액과 같거나 더 많을 경우에 실행할 수 있다. 위와 마찬가지로 경고창을 띄어줌.
    👉handler: @escaping () -> Void
    먼저 위의 handler가 실행 되면 아래 코드의 handler(amount)에 int를 넣어준다. 이 함수를 이용해 바깥에서 이용할 수 있다. 그래서 도망간다는 escaping이라는 말~

어떻게 쓰이는지 이해한 문법들 🧐

tag

코드에 label을 추가하고 다른 곳에서 텍스트라던가, 색을 바꾸고 싶은 경우 해당 태그 번호를 가진
새로운 객체를 만들고, 설정을 바꾸면 이용가능.

사용방법

  • 태그 번호 입력 ( 입력안할시 default : 0 )
  • 어떤 태그가 붙은 오브젝트가 필요하면 태그 번호로 객체 생성하여 처리
  • label를 코드로 추가
  • Button 을 StoryBoard에 추가 및 연결

Button에 lable 나타내기

// ViewDidLoad
// label 생성
let label = UILabel()
// 기본 셋팅
label.frame.size = CGSize(width: 100, height: 50)
label.text = "yjpotato!"
label.textColor = .black
label.center = self.view.center

// 태그 지정
label.tag = 1 

// 화면에 추가
self.view.addSubview(label)
@IBAction func btnTagText(_ sender: UIButton) {
        
        // 1.Tag로 지정
        let label = self.view.viewWithTag(1) as! UILabel
        
        label.text = "yjpotato!"
        label.textColor = .blue
    }

MainView에서 tag 사용하기

@IBOutlet weak var mainView: UIView!
@IBOutlet weak var subView: UIView!
@IBOutlet weak var lbTagLable: UILabel!

override func viewDidLoad() {
    label.tag = 1
}
// Tag로 지정
let label = mainView.viewWithTag(1) as! UILabel

label.text = "yjpotato!"  

tag 사용코드

// OrderTableViewCell.swift  
import UIKit

protocol OrderTableViewCellDelegate: AnyObject {
    func didTapStepper(amount: Int, quantity: Int, tag: Int)
}

class OrderTableViewCell: UITableViewCell {
    
    // MARK: - Properties
    @IBOutlet weak var mainImageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
    @IBOutlet weak var countLabel: UILabel!
    @IBOutlet weak var quantityStepper: UIStepper!
    
    weak var delegate: OrderTableViewCellDelegate?
    
    private var menu: Menu?
    
    var quantity = 0 {
        willSet { countLabel.text = String(newValue) + "개" }
    }

tag 호출

// OrderViewController.swift  
extension OrderViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return Menu.allCases.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "OrderTableViewCell", for: indexPath) as? OrderTableViewCell else { fatalError() }
        let menu = Menu.allCases[indexPath.row]
        let quantity = quantities[indexPath.row]
        cell.tag = indexPath.row
        cell.setData(menu: menu, quantity: quantity)
        cell.delegate = self
        return cell
    }

willset

willset은 프로퍼티 옵저버이다. 프로퍼티 값이 변경된 직후를 감지하는 것으로,
Model에서 갱신된 값을 View에 보여줄 때 사용하게 된다.
💡 클래스의 init()안에서 값을 할당할 때는 willSet이 호출되지 않는다. (초기화 이후부터 프로퍼티를 감시하기 때문!)

willset 사용 코드

//OrderViewController.swift
 
var totalPrice = 0 {
        willSet { totalPriceLabel.text = "\(newValue.toDecimalFormat())원" } // 총 결제 금액이 바뀔때마다 View의 Label을 업데이트 
    }
    
    var wallet = 0 {
        willSet { walletLabel.text = "\(newValue.toDecimalFormat())원"}
    }  // 내 지갑 금액이 바뀔때마다 View의 Label을 업데이트 
//OrderTableViewCell.swift  
 weak var delegate: OrderTableViewCellDelegate?
    
    private var menu: Menu?
    
    var quantity = 0 {
        willSet { countLabel.text = String(newValue) + "개" }
    }  //// stepper를 눌렀을때 Label을 업데이트 

이번 과제로 느낀점!

1.강의 꼭 복습하면서 헷갈리면 다시 보기
2.책 공부하기
3.모르면 꼭 물어보기..!

profile
나는 코딩하는 감자다!

0개의 댓글