개발하는 뚝딱이

[iOS] 환율계산기1 본문

iOS 개발일지

[iOS] 환율계산기1

개발자뚝딱이 2020. 3. 24. 11:53

환율 계산기 만들기

github 주소 : https://github.com/TheSongOfSongs/Currency-Calculator

 

 


환율계산기 프로젝트 시작 이유

새로운 프로젝트는 API를 이용하는 것에 목적이 가장 크다. 그리고 계산기의 디자인이 너무 날것이라 (내가 할 수 있는) 최선의 디자인을 만들어보고 싶었다. 원래는 상영관 조회 API를 만들고 싶었다. CGV, 롯데시네마, 메가박스 등 모든 상영관의 영화 시작 시간과 잔여석을 확인하고 싶었으나 쓸 수 있는 API가 없었다. 크롤링하여 데이터베이스에 저장해 뽑아올까 고민했으나, API 사용도 아니며 불법이기에 다른 API를 찾아보았다가 한국수출입은행의 환율 조회 API를 발견하였고 그렇게 시작하게 되었다.

 

환율계산기 디자인

 

PPT로 간단하게 디자인을 만들어보았다. 아래는 탭바로 구성하였으며, 각 이모티콘들은 무료 아이콘 사이트에서 찾아 가져왔다. 메인페이지에서 나라 선택은 검색 형식이 아닌 PickerView 피커뷰를 선택하였다. 검색을 하면 새로운 창을 보여줘야 하는데 사용자로서 느낄 때 뷰가 많이 전환되는 것을 별로 좋아하지 않기 때문이다. 환율우대와 매매기준율, 나라 선택 모두 PickerView를 사용한다.

 

원래는 '=' 기호를 누르면 환전금액을 조회하려고 했으나, 사용자가 본인이 확인 버튼을 눌렀는지 혼란을 줄 것 같아 키보드를 누를 때마다 환전된 금액을 쌍방향으로 보여주기로 결정하였다.

 

근처 환전소 페이지는 아직 디자인을 하지 않았다. 지도를 사용하는 부분인데 아직까지는 목표하기에 멀다고 느껴졌기 때문이다. 우선 먼저 메인과 나라별 환율 두 페이지를 완성하고 근처 환전소 부분에 대한 계획을 세울 것이다.

 

 

구현 내용

1. TextField를 이용하여 PickerView 보여주기

환율우대, 매매기준율, 나라선택, 금액까지 모두 UITextField를 이용하였다. Xcode에서 라이브러리 추가를 통해 가져오는 PickerView는 그 자체를 들고 오는 것이므로 위의 그림처럼 클릭했을 때 보여지도록 할 수 없었다. 그래서 버튼을 눌렀을 때 PickerView가 보여지도록 구현하려 생각하였으나 검색했을 때 다른 개발자들은 버튼이 아닌 UITextField를 이용한다고 하였다. 검색을 참조하여 쉽게 해결할 수 있었다.

 

UIPickerViewDataSource를 통해 PickerView에 보일 데이터 목록을 가져오고, UIPickerViewDelegate를 통해 데이터의 개수와 해당 row 내용, 그리고 선택된 데이터의 처리를 구현하였다. 그리고 '완료' 버튼을 누르면 PickerView가 없어지도록 툴바를 추가하였다.

    func dismissPickerView(textField: UITextField!) {
        let toolBar = UIToolbar()
        toolBar.sizeToFit()
        let doneButton = UIBarButtonItem(title: "완료", style: .done, target: self, action: #selector(self.dismissKeyboard))
        let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        toolBar.setItems([flexibleSpace, doneButton], animated: false )
        toolBar.isUserInteractionEnabled = true
        toolBar.updateConstraintsIfNeeded()
        
        textField.inputAccessoryView = toolBar
    }

툴바에 추가된 아이템은 flexibleSpace와 doneButton 두 가지인데, flexibleSpace은 이후에 추가하였다. '완료' 버튼이 툴바의 왼쪽에 위치하여 사용자에게 익숙한 UI가 아니라 생각되었다. 따라서 보이지도 않고 작동도 하지 않는 버튼을 하나 추가하여 '완료'버튼을 왼쪽으로 밀어버렸다.

 

도전과제

여기서 구현하며 힘들었던 점은 레이아웃에 문제가 발생한다는 것이다. 디버깅 결과, toolbar를 추가하면서 아래의 문장들이 콘솔에 찍혔다. (아래 더보기 참조) 스토리보드에 보이는 것도 아니고 직접 프로그래밍으로 오토레이아웃을 코딩한 것도 아닌데 어떻게 해결해야 할지 몰라서 이리저리 검색했다. stackoverflow를 참고했을 때는 보통 신경쓰지 말라라고 말하는데 찜찜해서 돌아다니다가 우연히 해결책을 찾았다. toolBar.updateConstraintsIfNeeded()를 추가해주어 서로 부딪치는 제약조건을 정리하도록 하였다.

https://github.com/onmyway133/blog/issues/538

더보기

2020-03-24 11:53:13.288203+0900 CurrencyCalculator[1205:39997] [LayoutConstraints] Unable to simultaneously satisfy constraints.

Probably at least one of the constraints in the following list is one you don't want. 

Try this: 

(1) look at each constraint and try to figure out which you don't expect; 

(2) find the code that added the unwanted constraint or constraints and fix it. 

(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 

(

    "<NSAutoresizingMaskLayoutConstraint:0x6000035da2b0 h=--& v=--& _UIToolbarContentView:0x7ff04b70d850.width == 0   (active)>",

    "<NSLayoutConstraint:0x6000035a50e0 H:|-(0)-[_UIButtonBarStackView:0x7ff04e115c40]   (active, names: '|':_UIToolbarContentView:0x7ff04b70d850 )>",

    "<NSLayoutConstraint:0x6000035a7930 _UIButtonBarStackView:0x7ff04e115c40.trailing == _UIToolbarContentView:0x7ff04b70d850.trailing   (active)>",

    "<NSLayoutConstraint:0x6000035d95e0 'TB_Leading_Leading' H:|-(20)-[_UIModernBarButton:0x7ff04b43ff90'\Uc120\Ud0dd']   (active, names: '|':_UIButtonBarButton:0x7ff04b43f550 )>",

    "<NSLayoutConstraint:0x6000035d9630 'TB_Trailing_Trailing' H:[_UIModernBarButton:0x7ff04b43ff90'\Uc120\Ud0dd']-(20)-|   (active, names: '|':_UIButtonBarButton:0x7ff04b43f550 )>",

    "<NSLayoutConstraint:0x6000035d9c70 'UISV-canvas-connection' UILayoutGuide:0x600002f9c000'UIViewLayoutMarginsGuide'.leading == _UIButtonBarButton:0x7ff04b43f550.leading   (active)>",

    "<NSLayoutConstraint:0x6000035d9cc0 'UISV-canvas-connection' UILayoutGuide:0x600002f9c000'UIViewLayoutMarginsGuide'.trailing == UIView:0x7ff04b446040.trailing   (active)>",

    "<NSLayoutConstraint:0x6000035d9d10 'UISV-spacing' H:[_UIButtonBarButton:0x7ff04b43f550]-(0)-[UIView:0x7ff04b446040]   (active)>",

    "<NSLayoutConstraint:0x6000035a53b0 'UIView-leftMargin-guide-constraint' H:|-(0)-[UILayoutGuide:0x600002f9c000'UIViewLayoutMarginsGuide'](LTR)   (active, names: '|':_UIButtonBarStackView:0x7ff04e115c40 )>",

    "<NSLayoutConstraint:0x6000035a5770 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x600002f9c000'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (active, names: '|':_UIButtonBarStackView:0x7ff04e115c40 )>"

)

 

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x6000035d9630 'TB_Trailing_Trailing' H:[_UIModernBarButton:0x7ff04b43ff90'선택']-(20)-|   (active, names: '|':_UIButtonBarButton:0x7ff04b43f550 )>

 

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.

The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

비록 활용은 잘 못했으나, 참고하면 좋은 사이트도 발견하였다. 위와 같은 콘솔 로그를 아래 사이트에 복붙하면 알기 쉽게 보여준다.

https://www.wtfautolayout.com/

 

 

2. PickerView가 키보드를 가리는 현상

그림의 미국 화폐 금액을 클릭하면 숫자 패드가 올라오면서 화면을 textField를 가리는 현상이 발생한다. 이때는 '키보드가 올라오면'이라는 조건에 따라 '화면이 위로 올라가도록' 구현하였다. 여기서는 NotificationCenter라는 개념을 배웠는데 Callback과 Delegate와 함께 객체 간의 소통을 담당한다. 이 부분의 개념은 iOS 개념 카테고리에서 다루고 일단 구현은 다음과 같이 한다.

    func createPicker(pickerView: UIPickerView, textField: UITextField!, dataArr: [String]) {
        pickerView.delegate = self
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
        textField.inputView = pickerView
        if textField == secondCountryText {
            textField.text = dataArr[12]
        } else {
            textField.text = dataArr[0]
        }
        textField.tintColor = UIColor.clear
    }
    
    @objc func keyboardWillHide(_ sender: Notification) {
        self.view.frame.origin.y = 0
    }
    
    @objc func keyboardWillShow(_ sender: Notification) {
        if secondCountryText.isEditing || secondCountryMoney.isEditing {
            self.view.frame.origin.y = -100
        }
    }

NotificationCenter 클래스의 addObserver 메소드를 이용한다. ViewController가 계속해서 듣고 있다가 키보드가 생길때와 사라져야 할 때, keyboardWillShow(_:)함수와 keyboardWillHide(_:)를 실행한다. keyboardWillHide는 작성 안해도 된다고 상관없다지만 에러가 나서 작성해주었다. #selector를 통해 실행하는 함수는 object-C이여야 하므로 swift 파일 내에선 @objc를 붙여야 한다. (이 부분에 대해서도 좀 더 찾아봐야겠다) 

 

이 때 keyboardWillShow(_:)함수에서 조건문을 통해 현재 작성하고 있는 textField가 아래에 위치한 나라와 환전할 금액인지 확인하고 맞다면 전체 화면을 위로 올리도록 하였다. 더 좋은 방법이 왠지 있을 것 같지만 일단은 여기까지 구현하고 넘어갔다.

 

3. 아래에만 테두리 설정하기

firstCountryText.layer.addBorder(padding: 5, widthPadding: 0)

extension CALayer {
    func addBorder(padding: CGFloat, widthPadding: CGFloat) {
        let border = CALayer()
        border.frame = CGRect.init(x: 0, y: frame.height + padding, width: frame.width - widthPadding, height: 0.5)
        border.backgroundColor = UIColor.lightGray.cgColor;
        self.addSublayer(border)
    }
}

정말 공부해야할 게 많군. 레이어도 따로  시간내서 찾아봐야할 부분. 환율우대와 매매기준율의 경우 textField 테두리가 없고 아래에 밑줄이 있다. textField의 border style을 선 없는 것으로 설정하고, 레이어를 통해 아랫 부분에 밑줄을 추가하였다.

 

 

4. 텍스트필드에 이미지와 padding패딩 추가하기

3번, 아래에만 밑줄 추가하기와 더불어 가장 많은 고민을 한 부분이다. 초록색 세모 이미지를 텍스트필드의 오른쪽 끝에 추가하고 국기를 왼쪽 끝에 추가하기 위해 몇 가지를 생각했다.

  • textField와 별개로 imageView를 설정하여 서로 겹치게 한다. 글자는 패딩을 주어, 국기만큼 공간을 띄우도록 한다 → 국기와 국가명이 세트느낌이 나지 않는다.
  • view를 만들어서 imageView와 textField를 서브뷰로 묶어 넣는다 → 시도했는데 생각보다 잘 안되더라
  • textField에 leftView로 이미지 추가 → border랑 붙어서 나옴 → 적절한 padding 추가
extension UITextField {
    
    func setLeftIcon(icon: UIImage) {
        let padding = 5
        let size = 45
        
        let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size + padding, height: 20))
        let iconView = UIImageView(frame: CGRect(x: padding, y: 0, width: 35, height: 20))
        iconView.image = icon
        outerView.addSubview(iconView)
        
        leftView = outerView
        leftViewMode = .always
    }
    
    func setRightIcon() {
        let size = 30
        let outerView = UIView(frame: CGRect(x: 0, y: 0, width: size, height: 20))
        let iconView = UIImageView(frame: CGRect(x: 0, y: 5, width: 13, height: 10))
        iconView.image = UIImage(named: "triangle")
        outerView.addSubview(iconView)
        
        rightView = outerView
        rightViewMode = .always
    }
    
    func setRightPadding() {
        let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 34, height: self.frame.height))
        rightView = paddingView
        rightViewMode = ViewMode.always
    }
}

extension을 통해 구현하였다. setLeftIcon(icon)은 왼쪽에 국기를 넣기 위해, setRightIcon()은 오른쪽에 초록색 세모를 넣기 위해, setRightPadding()은 화폐단위 '원' 만큼 공간을 띄우기 위해서 넣었다. '원'이 텍스트필드와 세트가 아니라서 아쉽다.

 

5. 커스텀 글꼴 사용하기

https://zeddios.tistory.com/199

 

iOS ) 왕초보를 위한 프로젝트에 Custom Font추가하는 방법

안녕하세요 :) 오늘은 Xcode에!!!나만의 폰트를 추가하는 방법을 알려드리겠습니다. System폰트는...흠흠.. 안이쁘니깐.. 아무튼 폰트추가하는 방법을 알려드릴게요! ㅅ ㅣ작! 프로젝트에 Custom Font추가하는 방..

zeddios.tistory.com

위 글을 참고하였다. (실수로 Target Membership을 체크하지 않아 꽤 오랜 시간 삽질한 기억이...)

 

 

 

마무리

이후에는 나라 선택 시, 국기와 화폐단위도 함께 변경하는 것을 구현하고 여유가 된다면 API까지 써서 계산 기능까지 만들고 싶다. 2월 중순부터 swift 문법을 익히며 iOS를 공부하기 시작했는데 겨우 한 달 밖에 안됐지만, 역시 갈 길이 멀다. 마음은 초조하고 공부할 것은 산떠미. 공부 방향을 잡아주는 가이드가 없어 불안한 마음이 든다. 하지만 현재 공부하기로 한 것에 꾸준히 노력한다면 많이 성장할 것이다. 화이팅!