2016년 1월 10일 일요일

UITextField 오른쪽 아이콘 버튼 (돋보기 등) 의 표시하기



위 그림에서 처럼 TextField의 돋보기 버튼 같은 이미지를 추가 하고 클릭 했을때
이벤트를 처리를 위해서는 아래와 같은 코드를 이용한다.

        //텍스트 필드 돋보기 표시
        let magnifyingGlassButton = UIButton()
        let image = UIImage(named: "ic_input_search_n")
        magnifyingGlassButton.setBackgroundImage(image, forState: .Normal)
        magnifyingGlassButton.frame = CGRectMake(0,0, 36, 36)
        magnifyingGlassButton.addTarget(self, action: "magnifyingGlassButtonClick", forControlEvents: .TouchUpInside)
        inputKeywordTextField.rightView = magnifyingGlassButton
        inputKeywordTextField.rightViewMode = UITextFieldViewMode.Always
        

        inputKeywordTextField.delegate = self


    //돋보기 클릭
    func magnifyingGlassButtonClick() {
        //검색 시작
    }

커스텀 버튼을 하나 설정 한다. 
이미지를 할당 하고 이벤트 함수를 addTarget으로 설정 한다.
텍스트 필드에 표시될 버튼이 클릭 되었을때 처리할 이벤트 코드를 작성한다.

해당 이벤트를 
TextField의 rightView에 버튼을 할당 한다.
나와야 하는 상황에 따라 rigthViewMode를 설정 한다.

간단한 팁 끝!

2016년 1월 9일 토요일

UITextField의 inset 적용하기

TextField의 입력 글자의 시작 위치를 어느 정도 들여 쓰기를 하는 방법이 IB로 처리 할 수 없다.
하지만 이러한 디자인요구사항은 있다.


import UIKit
@IBDesignable
class FormTextField: UITextField {

    /*
    // Only override drawRect: if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func drawRect(rect: CGRect) {
        // Drawing code
    }
    */
    @IBInspectable var inset: CGFloat = 0
    
    override func textRectForBounds(bounds: CGRect) -> CGRect {
        return CGRectInset(bounds, inset, inset)
    }
    
    override func editingRectForBounds(bounds: CGRect) -> CGRect {
        return textRectForBounds(bounds)
    }

}
inset을 처리 하는 코드를 별도의 UITextField 를 상속 받아 클래스를 선언 한다.

@IBDesignable와 @IBInspectable var 로 선언을 한
inset은 오른쪽 Utitlity 창에 표시 된다.



이렇게 만든 TextField 컨트롤은 글씨 들여 쓰기가 가능 하다.

아래의 그림의 Placeholder 텍스트의 표시를 보면 12픽셀 들여 쓰기가 되어 있다.





swift4
import UIKit
@IBDesignable
class FormTextField: UITextField {

    @IBInspectable var inset: CGFloat = 0
    
    //let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    
    override open func textRect(forBounds bounds: CGRect) -> CGRect {
        return UIEdgeInsetsInsetRect(bounds, UIEdgeInsets(top: 0, left: inset, bottom: 0, right: inset))
    }
    
    override open func placeholderRect(forBounds bounds: CGRect) -> CGRect {
        return UIEdgeInsetsInsetRect(bounds, UIEdgeInsets(top: 0, left: inset, bottom: 0, right: inset))
    }
    
    override open func editingRect(forBounds bounds: CGRect) -> CGRect {
        return UIEdgeInsetsInsetRect(bounds, UIEdgeInsets(top: 0, left: inset, bottom: 0, right: inset))
    }
    

}


2016년 1월 8일 금요일

Dropbox를 이용한 iOS 테스트 버젼 adhoc 배포

준비물 
Dropbox 
Device uuid


애플 개발자 페이지에 Devices에 배포해야할 사람들의 iOS 기기의 UUID를 모두 입력 해 둔다.
이 디바이스 목록을 프로비져닝에 추가되어야 해당 단말기에서 동작 한다.






테스트 배포를 하려는 앱에서 Product -> Archive 를 선택한다. 
타겟이 시뮬레이터인 경우 메뉴가 disable 되어 있다. 
Generic iOS Device이거나 케이블에 iOS 디바이스를 하나 붙여서 해당 타켓으로 변경 해야 메뉴가 활성 된다.

Archives  창이 뜨고 오른쪽에 Export 버튼을 누른다.

두번째 Ad Hoc Deployment를 선택 한다.

개발자 프로그램 계정을 선택 해 준다.



프로비져닝에 UUID가 입력된 프로비져닝이 있기 때문에
그냥 첫번째 것으로 넘어 간다.

최초 작업을 진행 할때는 첫번째 항목을 체크 한다. .plist 파일을 생성 하는 것이다.



APP URL, Display 이미지등이 없으나 넘어 가려면 https:// 라도 입력해야 Export 버튼이 활성 된다.




데스크탑에 해당 .ipa 파일과 manifest.plist 파일이 생성된다.
이제 이파일들을 dropbox에 특정 폴더에 복사를 하고
index.htm 파일을 만들면 된다.

index.html파일에 내용은
manifest.plist

파일을 다운로드 받는 URL 링크를 만들면된다.


!<li><a href="itms-services://?action=download-manifest&url=https://dl.dropboxusercontent.com/s/w9ysszgqwxepx3f/SmartBand.plist">OTA 설치</a></li>

위 예제의 URL은 dropbox에서 



 





를 통해서 얻은 URL을 다음과 같이 변경 해야 한다.

https://www.dropbox.com/s/w9ysszgqwxepx3f/SmartBand.plist?dl=0
-->
https://dl.dropboxusercontent.com/s/w9ysszgqwxepx3f/SmartBand.plist


ipa 파일에 대한 링크를 얻은 다음 해당 내용은 plist 파일에 내부에 편집을 해야 한다.

.plist의 ipa를 가리키는 URL도 https://dl.dropboxusercontent.com/s 로 변경 해야 한다.

그런다음 해당 테스트 앱을 받아야 하는 고객에게는 index.htm에 대한  링크를 전달 하면 된다.

iOS에서 해당 링크를 클릭 하면 dropbox앱을 통해서 링크가 열리고 사파리 브라우저에서 해당 링크를 
클릭 하면 앱이 다운로드되고 설치 된다.


2016년 1월 7일 목요일

textfiled 입력을 위한 keyboard , pickerview 에 Cancel, Done 버튼 추가하기

어제의 post 에서 http://mtsparrow.blogspot.kr/2016/01/swift-delegate.html

문제를 처리하기 위해 잘못 접근 했다.

ViewController는 입력 TextField가 있고
Keyboard 대신 PickerView가
그리고 화면은 dimming이 되는 이러한 ViewController를 프로그래밍 하기 위한 방법으로

Storyboard 에 TextField 를 디자인 하고
별도의 여러 View 를 .xib로 생성해서 각각 PickerView를 디자인 하고 배경은  blackColor에 opacity를 80%주어서 만든 커스텀 뷰를 올리고 거기에 Done 버튼과 Cancel 버튼을 delegate로 처리 하려고 했다.
TextField delegate 메서드에서 이 커스텀 뷰를 보이게 사라지게만 처리하면 끝이다.

그런데 헐!

키보드?? 어찌합니까? 어떻게 할까요?



PickerView와 Cancel, Done 버튼을 추가 해서 별도의 View로 만들고 그것을 계속 사용하면 되겠지 하며 디자인 가이드에 다음 페이지를 넘기는 순간 ? 헐 이런 숫자 Keyboard?? 그 위에 Cancel, Done 버튼

그렇다. 문제의 접근을 처음부터 잘못 이해 하고 덤벼 들었다.

iOS에서는 키보드위에 카메라 아이콘부터 많은 여러가지 기능을 추가 하도록 디자인 되어 있다.

키보드는 별도의 컨트롤로 디자인 타임에 할 수 있는 컨트롤이 아니다. 단지  TextField의 속성이다.



숫자 키보드가 나오게 하려면 TextField의 속성을 변경 해 주면 된다.

코드에서 textField의 inputAccessoryView에 toolbar를 붙이면 그게 키보드 위에 Cancel, Done 버튼 같은 것을 언질수 있다.

    func addCancelDoneButton() {
        let keyboardToolbar = UIToolbar()
        keyboardToolbar.sizeToFit()
        
        let cancelBarButton = UIBarButtonItem(barButtonSystemItem: .Cancel,
            target: view, action: Selector("cancelEditing:"))
        let flexBarButton = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace,
            target: nil, action: nil)
        let doneBarButton = UIBarButtonItem(barButtonSystemItem: .Done,
            target: view, action: Selector("endEditing:"))
        keyboardToolbar.items = [cancelBarButton, flexBarButton, doneBarButton]
        excerTextField.inputAccessoryView = keyboardToolbar
    }

  
간단 하게 toolbar에 버튼 2개를 추가하고  excerTextField.inputAccessoryView = keyboardToolbar
를 하면 키보드 위에 버튼이 올라간다.

그럼 PickerView도?


물론 PickerView에 addSubView로 toolbar를 언저 보니 올라 간다.
이렇게 하면 또 삽질을 하는 것이다.
PickerView에 툴바를 붙이는 것도 PickerView.addSubView()로 하고
textFiled에서 PickerView를 보이게 하는 방법 또한 잘못된 접근이다.

textField에서 키보드 대신 pickerview를 보이게 하는 방법은 다음과 같다.
코드로 picker뷰를 생성하고
heightTextField.inputView = picker
마찬가지로 툴바를 생성 하고
heightTextField.inputAccessoryView = toolBar

이렇게 하면 TextField 선택시 pickerView가 나오고 pickerView 위에 cancel , done 버튼이 추가 된다.

단점

단점은 여러 입력 TextField의 각각의 PicekerView가 나와야 한다면
PickerView delegate  함수 내부가 각각의 pickerview에 따라 보여 줘야 하는 데이터부터 처리 하는 내용이 복잡 해지는 단점이 있다.
하지만 하나의 ViewController 클래스에서 모두 처리 가능 하다.

무지에서 시작된 어제의 삽질을 오늘 되돌린다.

마지막 키보드나 피커뷰가 나오면 전체 화면이 dimming 처리 하는 것은 어떻게?


다른 방법이 있을지 도 모르지만 이건 아주 간단하게

전체 화면 dimming  뷰를 만들고 이를 textfiled 편집 시작 시 보이게 하고, textfiled 편집이 종료되면
사라지게 하는 방법을 하면 된다.

var dimmingView : UIView!

viweDidLoad() {
....
dimmingView = UIView(frame: CGRectMake(0, 0, view.frame.width, view.frame.height))
        dimmingView.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.2)
        
        self.view.addSubview(dimmingView)
        dimmingView.hidden = true

func textFieldShouldBeginEditing() {
dimmingView.hidden = false

func textFieldShouldEndEditing() {
dimmingView.hidden = true

2016년 1월 6일 수요일

swift delegate 이해하기

간단하게 swift delegate 를 이해하기


C 프로그래머 입장에서 그냥 간단하게 콜백함수로 이해 하는게 가장 쉽다.

언제쓰냐?

간단하게 예제를 생각해 보면

ViewController에 TextFiled가 있다. 날짜만 입력 받아야 하는 경우

여기 편집이 시작되면 keyboard가 아니라 PickerView가 나오고
여기서 선택된 row가 TextField에 입력 되야 한다.




대충 위와 같은 디자인 
이것은 ViewController 위에 Black 컬러에 20%투명을 준 전체 화면 View에 하단에  DatePicker 그리고 Cancel, Done 버튼을 지닌 별도의 View를 위에 언지는 것이다.



PickerView 가 같은 ViewController에 존재 한다면 문제가 없지만
PickerView를 디자이너님 입맛에 맡도록 별도의 View로 만들고 별도의 View class 코드가 존재 한다면


addSubView 로 이미 PickerView가 add되어 있고 ViewController에서 hidden으로 존재 하다가
TextField가 편집 시작되면 keyboard대신 hidden 속성을 false로 해서 나오게 한다.


    func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
        datePickerView.hidden = false
    }

그러면 datePickerView 라는 pickerview를 가지고 잇는 view가화면에 출력 되었다.
모든 제어가 여기로 넘어가서 날짜를 선택 했다.

그 선택된 문자열이 textField.text에 입력이 되야 한다.

datePickerView.DateString이 결과 값이라고 할때 이것을 언제 어떻게 assign 해야 하나?

이럴때 delegate가 필요 하다.

datePickerView의 클래스

protocol DatePickerViewDelegate {
    func DatePickerDidSetDate()

}

영어가 짧은 관계로 DidSetDate?? 이렇게 delegate 함수 이름을 지었다.

var delegate: DatePickerViewDelegate?
이렇게 deleage를 선언 해준다.

self.DateString = strDate
        delegate?.DatePickerDidSetDate()

picker의 결과를 설정 한 다음 delegate 메서드를 호출 해 준다.

이제 이 View를 이용하는 ViewController Class

class ProfileViewController: UIViewController, UITextFieldDelegate, DatePickerViewDelegate
{
var datePickerView: DatePickerView!

....

datePickerView.delegate = self


...
// MARK : DatePickerViewDelegate
    func DatePickerDidSetDate() {
        birthdayTextField.text = datePickerView.DateString
    }

pickerView에서 델리게이트 함수를 호출해 주면 ViewController에 DatePickerDidDate() 함수가 호출 되고 여기서 TextField.text를 설정 하면 된다.

이런 메카니즘이 UITextFieldDelegate, UITableViewDelegate  등등... 우리가 자주 사용하는 delegate 함수를 작성해야 하던 일을 직접 만든 ViewClass에서 이용하는 방법이다.





2016년 1월 4일 월요일

UIProgress View 인터페이스 빌더에서 height 문제점

인터페이스 빌더에서 UIProgress를 뷰컨트롤러에 올리면 황당한 일이 벌어진다.

height 값이 2로 고정되어 증가 시킬 수 가 없다.



물론 현재 Xcode 7.2에 버그로 생각된다.



원래 컨트롤로 기본 모양도 이렇지 않은데 이게 왜 이럴까?




어째든 디자이너는 progress이미지와 백그라운드 이미지를 주었기 때문에
원하는 결과물을 얻어야 한다.

주어진 이미지 리소스를 슬라이싱 해서 설정해도 heigth를 19로 키우지 못하면 안된다.




해결 방법은 나의 경우 Autolayout의 컨스트레인트를 강제로 height를 준 것이다.




그래도 여전히 문제점은 있다. 19로 강제로 설정해서 한다.
Autolayout  워닝이 계속 유지된다. 이부분은 아무래도 버그!
어짜피 처음부터 height를 변경 못하는 거 자체가 버그!





런타임에 최종 결과물은 위와 같다!























2016년 1월 3일 일요일

iOS 구동 동영상을 만들고 싶을때?

앱을 앱스토어에 올리기전에 스크린샷과 설명이 필요 하다.

요즘에는 동영상도 올리기도 한다.

아이폰 구동 동영상을 만드는 방법은 어떻게 하는 것 일까?
AirServer를 이용하는 방법과 QuickTime Player를 이용하는 방법이 있다.
AirServer는 오래전에 앱을 구매하고 알게된 기능이고 QuickTime Player는 무료로 이용이 가능하다.

QuickTime Player를 이용하는 방법이 훨씬 좋다.




1.Air서버 + AirPlay 미러링 



먼저 Mac 컴퓨터에서 AirServer를 실행 한다.




iOS 에서 AirPlay  미러링을 시작 한다.





Mac  컴퓨터에 미러링이 시작되면 하단에 빨간색 녹화 버튼으로 녹화를 할 수  있다.





단점 : 유료앱 AirServer가 필요하다.


2. QuickTime Player 




QuickTime Player
기본으로 설치되어 있는 이앱이 이런 기능이 있는지 몰랐다.





iOS를 Mac과 케이블로 연결한다음 새로운 동영상 녹화를 선택 한다.






하단 녹화 버튼 옆 콤보 버튼을 누르면 iOS를 장치를 선택 할 수 있다.

그럼 iOS 화면이 디스플레이 되면서 구동 영상을 녹화 할 수 있다.