Skip to main content

5 posts tagged with "아이폰"

View All Tags

· 8 min read

검색기능을 추가할 일이 생겨서 여기저기 살펴봤으나 서점에 진열된 책들을 뒤져봐도 내가 필요한 부분을 찾지 못했다. 애플에서 제공하는 문서가 있기는 했으나 익숙하지 못한 나같은 초보에겐 그냥 문서에 불과했고 며칠간 고심하면서 인터넷을 뒤진 끝에 괜찮은 사이트를 발견. (아래 링크 참조) 복습도 할 겸 간단히 정리해본다.

1. 시작 / 준비

이 예제에서 설명에 사용하는 프로젝트는 기본적으로 Navigation based application으로 작성되어 개발자가 보여주고자 하는 어떤 리스트가 table view에 표시되는 application에서 시작한다. 목표로 하고 있는 완성 이후의 모습은 평상시엔 원본 데이터가 table view에 표시되고 화면 상단에 위치한 search bar에 찾고자 하는 문자열 입력 시 원본 데이터가 표시된 table view에 검색 결과가 나타나게 만드는 것이다. (아래 데모 동영상 참고) 그리고 원래의 navigation based application project의 Interface Builder에서 UISearchBar를 추가해 두어야 한다. 아래의 단계들을 거치고 나중에 UISearchBar의 Outlet과 delegate를 File's Owner로 설정하는 것 또한 기본적으로 해주어야 하는 작업.

demo

2. Header

원본 데이터를 저장하는 listOfItems와 검색된 결과를 저장할 copyListOfItems를 만들어야 한다. 실제 table view에 검색결과를 보여주고자 할 때는 copyListOfItems에 있는 내용을 보여주고 view를 reload 하면 끝. searchTableView는 실제 검색을 수행할 로직이 포함된 함수이고 doneSearching_Clicked는 검색 완료 후 누를 done 버튼 클릭시 호출될 함수이다.

3. searchBarTextDidBeginEditing

UISearchBarDelegate protocol에 포함되어 있는 함수로 UISearchBar에서 사용자가 어떤 문자열을 입력하려고 선택하면 자동으로 호출되는 함수이다. 이 함수가 자동으로 호출되기 위해서는 UISearchBar의 delegate가 File's Owner 혹은 다른 것으로 연결되어 있어야만 한다.

위의 코드에서 searching, letUserSelectRow 등은 상태를 알기 위해 사용할 flag 용도의 BOOL 멤버변수들이다. 많은 책에서 다루고 있는 예제들에서 종종 등장하는 navigationItem에 버튼 추가 루틴이 제일 하단에 포함되어 있다. Done 버튼을 추가하는 내용이고 Done 버튼 클릭시 doneSearching_Clicked 를 호출하라고 selector가 설정되어 있다.

3. textDidChange / searchBarSearchButtonClicked

UISearchBarDelegate protocol에 포함되어 있는 또 다른 함수 둘. 하나는 UISearchBar에 입력된 문자열이 변경되었을 경우 호출되는 함수이고 searchBarSearchButtonClicked는 UISearchBar가 focusing 되었을 경우 자동으로 나타나는 Soft keyboard의 Search 버튼을 클릭했을 때 호출되는 함수이다.

두 함수 호출시 모두 실제 검색작업을 수행해야만 한다. textDidChange에서는 사용자에 의해 입력된 문자열의 길이로 검색여부를 판단하고 (0일 경우에 검색하면 안되니까) searchBarSearchButtonClicked 에서는 단순히 검색 로직을 수행한다.

4. searchTableView

실제 검색 로직이 포함된 함수이다. 이 함수가 호출되었을 경우 검색결과 데이터를 저장할 copyListOfItems array의 내용을 업데이트 해주어야 한다.

위의 코드를 보면 rangeOfString이라는 함수를 사용하고 있는데 내용은 다음과 같다. 검색 대상이 되는 원본 데이터 listOfItems에 사용자가 입력한 searchText가 포함되어 있으면 그 문자열의 시작위치와 길이를 리턴하게 되는데, 길이가 0 이상이라는 얘기는 포함되거나 일치하는 부분이 있다는 의미이므로 그 문자열의 원본 데이터를 copyListOfItems에 추가한다.

5. numberOfRowsInSection

Table view를 사용하는 모든 프로젝트에 필수적으로 포함되어야 하는 함수로 몇개의 row를 보여줄거냐를 결정해 주어야 한다. 평상시에는 원본 데이터의 갯수를 표시해주면 되고, 검색시에는 검색된 데이터의 갯수를 리턴해야 한다.

6. cellForRowAtIndexPath

위의 row 갯수처럼 cell을 어떻게 표시할지도 결정해 주어야 한다. 위와 마찬가지로 평상시에는 원본 데이터의 문자열들을 표시해주면 되고, 검색시에는 copyListOfItems의 데이터들을 표시해주어야 함.

여기까지 해놓고 보면 기본적인 검색 기능은 제대로 동작을 한다. 내가 실제로 응용할 내용은 이것과는 조금 차이가 있지만 이런 기본적인 검색 기능을 살펴봐야 감이 와서...남은 오늘 하루는 어떻게 적용해야 하는지를 놓고 머리를 싸매야 한다. ㅠ

참조 사이트 : http://www.iphonesdkarticles.com/2009/01/uitableview-searching-table-view.html

· 7 min read

이번주에 연구회에서 책 Ch.3에 대한 내용을 발표하게 되어서 Ch.2 부터 정리했습니다. 원래 지난주에 Ch.3까지 다 정리하긴 했는데 그게 머릿속으로만 정리한거라 ㅎㅎ. Ch.2와 3이 거의 연결되는 내용이라 따로 분리할 필요는 없을거 같긴 한데 그래도...

Protocol

@interface InstaTwitViewController : UIViewController
<UIPickerViewDataSource, UIPickerViewDelegate> {
...
}

위의 예문에서 보면 특이하게 < > 기호로 감싼 부분이 있는데, UIPickerViewDataSource와 UIPickerViewDelegate 라는 protocol을 사용하겠다는 의미입니다. Protocol이란 개념이 Objective C 2.0 부터 포함되어 있는거 같은데, 다른 언어들만 접해본 입장에서는 생소한 개념입니다. 근데 뭐 간단히 생각해보면 일반적인 protocol을 보통은 '어떤 시스템에서 미리 약속해 놓은 규약' 정도의 의미로 사용하고 있으니 비슷하게 이해하고 넘어가면 되지 않을까 싶네요. Objective C의 특징과 연결해서 생각해보면 Objective C가 다중상속을 금지하고 있기 때문에 보완하고자 만들어진 개념이 아닐까 생각됩니다. 전 Java에서 Interface와 유사한 녀석이라 생각하기로 했습니다.

alloc & init

activities = [[NSArray alloc] initWithObjects : @"abc", @"def", nil];

Objective C에서는 new 대신 alloc이란 method (message)로 새로운 객체 생성을 하는 듯 합니다. 보통 class 들의 멤버들을 열어보면 보통 초기화 method들이 제공되는 듯 한데 초기화도 방법에 따라 다양한 녀석들이 있습니다. 위의 경우엔 (initWithObjects) 다른 객체들로 초기화하는 내용을 표현하고 있습니다. 위에서는 NSString type의 객체들이네요. 그리고 마지막은 nil (=null)을 넣어두고 있습니다. Objective C에서 제공하는 method 들은 대부분 이름만 봐도 대충 의미가 파악된다는게 참 좋은 점이라고 생각합니다. 근데 쓰면서 보니 마지막엔 꼭 nil을 넣어주어야 하는 것인지 갑자기 궁금해지네요. 이 내용은 확인해서 추가해야겠어요. 참 alloc으로 새로 할당한 녀석들은 꼭 나중에 release 해야 합니다.

참으로 헷갈렸던 method 이름

- (NSInteger)pickerView : (UIPickerView *)pickerView numberOfRowsInComponent : (NSInteger)component {
....
}

이거 전체가 method의 prototype입니다. 참 이상하고 헷갈리고, 책을 당장이라도 덮고 싶게 만드는 것들 중 하나입니다. 근데 한 번 이해하니 오히려 편한 부분도 있습니다. 그냥 보면 굳이 parameter로 무엇을 전달해야 하는지 reference를 찾아볼 필요가 없어요. :) 그래서 제 나름대로 이해하기 쉽게 배열하기로 했습니다. 그럼 더 편하거든요. 위의 method의 정확한 이름은 이렇게 표현할 수 있습니다.

(NSInteger)pickerView : numberOfRowsInComponent

pickerView만 이름인게 아니라 numberOfRowsInComponent까지 이름이 되는거죠. 이렇게 이해하면 됩니다. 원래 pickerView라는 녀석인데 numberOfRowsInComponent에 필요한 parameter를 하나 더 받을거다 라고요. 그래서 나름 쉽게 풀어서 다시 쓰면 아래와 같습니다.

(NSInteger)pickerView : (UIPickerView *)pickerView
numberOfRowsInComponent : (NSInteger)component {
....
}

다시 쉽게 해석해보면 첫번째 parameter로 UIPickerView의 pointer type인 pickerView를 가져야 하고, numberOfRowsInComponent라는 parameter가 필요한데, NSInteger type의 component란 이름으로 parameter를 받을거란 얘기입니다. 책에선 외부 parameter, 내부 parameter란 이름으로 얘기하고 있는데 공식적으론 numberOfRowsInComponent란 parameter가 필요하고 실제로 component란 이름의 parameter를 넘긴다고 이해하면 됩니다. 그러므로 method 내부에선 component를 가지고 처리하면 되겠습니다. numberOfRowsInComponent를 보면서 가만히 생각해보면 이게 어떤 component 안에 있는 row의 갯수를 어떻게 하겠다는 얘기니 필요한 component를 parameter로 받겠다는 걸 알 수 있습니다. 특이하게 보통 이런 경우 number of rows를 return하게 되더군요. 실제로 이 method는 특정 component를 parameter로 받아서 그 component의 row 갯수를 NSInteger type으로 return하도록 작성되어 있습니다. 설명이 좀 복잡해진 듯 하지만, 3개 이상의 parameter 들도 이런 식으로 작성되기 때문에 이런 형태에 대해 익숙해져야 할 듯 합니다.

3개의 parameter를 사용하는 예

- (NSString *)pickerView : (UIPickerView *)pickerView
titleForRow : (NSInteger) row
forComponent : (NSInteger)component {
....
}

위에서 설명한 것과 비슷한 방법으로 해석해보면 제일 끝에서부터, component를 받을거고 row도 필요하고 NSString * type의 값을 return 할거라는 걸 알 수 있고, 더 나아가면 특정 component를 받아서 component 별로 row를 입력받을 생각이고 결국은 해당 row의 title을 return 하겠다는 걸 추측해볼 수 있습니다.

· 5 min read

Ch.1, 2를 보면서 의문을 가지고 있었던 모든 것을 Ch.3에서 해결할 수 있습니다. Head First 시리즈라서 그런지 책 구성이 무조건 시작하고 따라하면서 복잡한 설명은 다음에 한다는 식으로 표현되어 있었는데, 진짜 약속대로 Ch.3에서 설명하고 있더군요.

Property

이게 뭔지 정말 궁금했었습니다. 궁금증을 이기지 못해 가지고 있던 Objective C 책을 뒤져서 Ch.2 공부하던 시점에 대강 알아두긴 했는데 Ch.3에서 친절하게 설명했더군요. 예문은 아래와 같습니다.

@interface InstaTwitViewController : UIViewController
<UIPickerViewDataSource, UIPickerViewDelegate> {
IBOutlet UITextField *notesField;
....
}

@property (nonatomic, retain) UITextField *notesField;
....
@end

보통 OOP를 공부하다보면, 멤버변수에 직접 접근하지 말고 getter와 setter를 만들어 간접적으로(indirection의 의미) 접근하는 방법을 사용하라고 권유하는 걸 많이 보게 됩니다. 근데 사실 getter와 setter 라는게 별개 없습니다. 남이 작성한 코드를 열어봐도 getter와 setter는 거의 같은 모습을 하고 있지요. 단지 사용되는 멤버변수들의 이름만 다를 뿐입니다. 그래서인지 Objective C에서는 이걸 자동으로 만들어주는 기능을 제공하고 있습니다. 그 기능이 바로 그동안 괴롭혀왔던 property 입니다. 위의 예문에서는 notesField라는 이름을 가진 UITextField 객체를 사용하고 있는데 읽거나 변경하는 작업을 하게 될 것 같습니다. 그래서 property로 만들어서 별도로 getter와 setter를 작성할 필요없이 자유롭게 사용할 수 있게 만들어 두었습니다. 유의할 사항은 property를 사용한다면 class 구현부분에 반드시 @synthesize로 명시해주어야 한다는 점입니다.

Property 속성 키워드

Property에 대해서 대강 이해가 되었지만 예문에 있는 nonatomic과 retain은 무엇이냐는 의문이 또 남게 됩니다. Objective C에서는 property로 getter와 setter를 만들어줄 때 속성을 지정할 수 있게 해 두었다고 합니다. nonatomic과 retain은 그 속성 중 일부인거죠. 책에서 설명하고 있는 속성들은 아래와 같습니다.

readonly : property가 변경되지 않을 때 사용 . 컴파일러가 setter 생성하지 않음.

retain : 객체를 다룰 때 사용. 객체를 계속 사용하겠다는 의미이며, 내부적으로 reference count를 증가시킴

readwrite : property를 변경할 필요가 있을 때 사용(default)

copy : property를 복사해 사용하겠다는 의미이며 전달된 원래의 값이 변경되지 않도록 할 때 사용됨

assign : int, float 등의 기본형을 다룰 때 사용. 단순히 할당하고자 할 때만 이용함(default)

nonatomic : 기본적으로는 property 값을 변경할 때 mutex를 사용하도록 되어있는데(atomic), multi-thread가 필요없을 경우 mutex 사용이 낭비이므로 불필요한 처리를 막을 때 사용

기타

NSString 객체들은 값 변경이 되지 않습니다. 만약 이미 할당한 문자열을 바꿀 일이 있다면 NSMutableString을 사용하면 문자열 변경이 가능합니다.

· 7 min read

책을 보고나서 정리한다는게 생각처럼 쉽지 않다. 특히 예제가 많거나 할 때는, 가장 좋은 방법이 직접 해보는거라 다 넣고 싶지만 너무 길어지게 되어서 고민된다. 그래서 이후로는 그냥 팁이나 문법적인 내용만 간략하게 정리할 생각이다. 어차피 대부분의 Objective-C 책들이 C 문법을 어느정도 알고 있다는걸 가정하고 쓰여졌으니까.

1. Indirection

책에선 간접적인 방법에 의한 참조를 indirection이라고 표현하고 있다. 코드에 직접 값을 입력하거나 하는 방법이 아닌 포인터 등을 이용해 접근하는 방법을 의미한다. 예를 들어 for 문을 돌리는데 원하는 반복횟수를 직접 숫자로 입력하는게 가장 간편한 방법이 되겠지만 이런 방법을 아무 생각없이 사용하게 되면 이후의 유지보수나 확장성 측면에서는 별로 좋지 않다. 그래서 함수 상단에 반복횟수를 저장하는 변수를 하나(예를 들어 흔히쓰는 count나 cnt 등으로) 두고 그 변수를 for문이나 함수 내의 다른 곳에서 사용하게 되면 수정할 필요가 있을 때, for 문이나 함수 전체를 훑어보고 수정할 필요 없이 저장된 변수의 값만 하나 살짝 바꿔주면 된다. 책에선 이런 식의 접근 방법을 indirection 이라하고 객체지향의 의미를 설명하게 전에 언급해 두었다. 실제 예는 아래에.

위의 그림은 word.txt 파일에서 문자열을 한줄씩 읽어 문자열과 함께 문자열의 길이를 출력해 주는 예이다. 이걸 indirection의 의미를 이용해 어떻게 바꿀 수 있을까? 파일명을 코드에 넣지 않고 인자로 입력받는 방법을 쓴다면 여기서 좀 더 나아간 코드가 될 것 같다는 느낌이 든다. 물론 위의 예제 자체도 indirection을 약간 사용한 내용이다. 파일에서 읽어오는 것이 아니라 문자열을 직접 코드 안에 넣었다면 유지보수나 확장이 더 힘든 코드가 될 것이다.(물론 때에 따라 코드 안에 직접 문자열을 넣어두는게 좋을 때도 있지만...) 어찌되었든 위의 코드를 조금 더 개선해보면 아래와 같이 된다.

main 함수 상단만 변경했다. 먼저 인자의 갯수를 판단해서 파일 이름을 넣어두었는지 판단한 후에 해당 인자를 파일 이름으로 해서 fopen을 하는 내용으로 변경되었는데, 책과 거의 비슷하게 만들어 보니 오류가 있다. 해당 파일이 없을 경우를 고려해서 파일이 없을 땐 아무 작업도 하지 않고 종료되게 해야 하는데 위의 예제는 fopen에 실패해도 그냥 fgets를 호출해 파일포인터를 사용하게 된다. 분명 잘못된 파일이면 run time error 가 발생할게 뻔하다. 하지만 어차피 indirection 을 설명하기 위한거니까 이해해 주시길. 이렇게 변경하면 최초의 예제보다는 좀 더 간편한 코드가 만들어진다.

2. XCode에서 argument 설정하기

두번째 예제를 만들어보면서 XCode에서 argument는 어떻게 설정하는지 알게 되었다. 바로 다음의 그림 참조.

코드가 보이는 편집기 말고 XCode 화면을 보면 왼쪽에 Executables가 보이고 화살표를 눌러 하위 내용을 보면 코드가 들어있는 m 파일이 빌드된 바이너리가 보인다. 그 바이너리를 더블클릭하면 아래와 같은 창이 열리면서 다양한 설정메뉴들이 나타난다.

상위의 탭에서 두번째에 위치한 Arguments를 클릭하면 처음엔 비어있는데 가운데쯤에 있는 + 버튼을 누른 후에 인자로 넣고 싶은 내용을 추가해주면 끝난다. 나의 경우엔 문자열이 들어있는 텍스트 파일이 빌드된 실행파일보다 두 단계 상위 디렉토리에 있었기 때문에 상대경로로 지정해 두었다. 여기까지 입력한 후에 콘솔창을 띄우고 Run 해보면 끝. 물론 txt 파일이 정상이어야 하고 인자로 입력한 위치/파일명과 동일한 파일로 이미 만들어져 있어야...^^

· 8 min read

특이한 문법 때문에 Objective C 책을 한 권 샀다. 시간날 때 마다 공부해서 정리할 생각. 책은 Apress에서 발간한 원서를 BJ 퍼블릭이란 곳에서 번역한 출판한 "(아이폰과 맥 OS X 개발을 위한) Objective C" 이다. (원래 서명은 굵은 꺽쇠(?)로 감싸줘야 한다고 배웠었는데, 맥북이란게 한글 친화적이지 못한 부분이 조금 있어서 어쩔 수 없이 큰 따옴표를...)

1. Hello World

누구나 언어를 처음 배우면 처음 해보는게 Hello World 출력하는 프로그램이다. 왜냐하면, 1.간단하고, 2.사용할 IDE의 기본을 후딱 맛보기 좋으며, 3.여러가지 이유로 각종 책들이 젤 첫 예제로 Hello World를 사용하기 때문이다. 아무것도 모르는 우리들이야 어쩔 수 있나 무작정 따라할 수 밖에.

IDE로는 OS X 설치된 맥만 있으면 무료로 다운받을 수 있는 Xcode. 무조건 실행해보면, Visual Studio나 Eclipse 등의 IDE들 처럼 프로젝트를 새로 만들거냐 라는 물음의 메뉴가 최상위. 기타 등등은 아래에 배치되어 있다. 새 프로젝트 만들겠다고 선택한 후

위 그림과 같은 화면이 나오면 왼쪽 메뉴에서 Application, 오른쪽에서 Command Line Tool, 중앙의 Type을 Foundation으로 선택한뒤 Choose 버튼을 꾹 누른다. 그리고 적당한 프로젝트 이름과 위치를 지정해주면 끝. Xcode가 뚝딱뚝딱 뭔가 처리하더니 아래와 비슷한 화면이 보이게 된다.

난 HelloWorld 라고 프로젝트 이름을 지정했더니 HelloWorld.m 파일이 보인다. 그게 Objective-C 소스가 들어갈 파일이다. 나머지 파일에 대해선 천천히 공부할 예정. 책에서 언젠간 언급이 되겠지. (난 시간이 없다. ㅎㅎ 언급이 안되더라도 언젠간 알아야 할 시점이 올거다. 아마도...) 더블클릭해서 원래 Xcode가 만들어준 소스를 지우고 아래처럼 입력해본다.

C와는 다르게 #import 라는게 있지만, 생김새보니 딱 #include 역할이다. stdio.h 대신 Foundation.h 가 쓰인다고 생각하면 될 듯 하다. 참고로 Foundation.h 파일의 위치는 /System/Library/Frameworks/Foundation.framework/Headers/ 이다. 분명 나중에 해당 헤더들을 한번쯤은 열어볼 필요가 생길 것 같다. C처럼 생각한다면...

헤더파일 import를 제외하고는 어색한 부분이 딱 하나 보인다.

NSLog(@"Hello World");

NS는 스티브 잡스가 애플에서 쫓겨났을 때 만들었던 NeXT 라는 회사에서 만든 NextSTEP의 약자라고 하는데, 저 앞부분만 제외하고 보면 Log니까 콘솔에 글자를 찍어주는 함수일거란 느낌이 든다. 근데 @는 뭘까? @는 뒤에 오는 문자열이 NSString 형태라는 알려주는 기호라고 한다. 그러니까 NSLog는 C의 printf와 비슷한데, printf에서처럼 일반 문자열을 사용하는게 아니라 NSString type을 사용하기 때문에 @를 넣어준다고 생각해주면 쉽다. printf 보다 편리한 점은, 뿌려주고 싶은 문자열 마지막에 개행문자(\n, \r\n)를 넣지 않아도 된다는 점. 빌드해보면 아래와 같은 결과를 얻을 수 있다. (Build + Run 은 Command + R이고, 이전에 콘솔 창을 먼저 열어줘야 하는데 단축키는 Command + Shift + R)

굵은 글씨로 Hello World 라고 떡하니 찍혀있다. 어찌보면 당연한건데 항상 처음하는건 신기하고 기쁘다.

2. %@, BOOL

좀 더 예제를 다뤄보기로 하자. 역시 전부 다 지우고 이리저리 입력해 보았다. 아래의 그림.

그냥 단순히 숫자 비교하는건데, 속도를 위해 main 함수를 보면 아래와 같은 구문이 있다.

NSLog(@"%d and %d are same, right? %@", 3, 5, boolToString(areSame(3, 5)));

printf 처럼 형식지정자가 비슷하다. int type 받을거니까 %d 사용했는데, %@ 라는 것도 있다. 직관적으로 NSString type을 받을 것임을 느낄 수 있다. 간단하다. 특이한 것 하나는 YES, NO 같은게 있다는건데 이건 뭘까? 바로 BOOL type 이다.

C에서도 bool type 이 있고 true, false 가 있으니 마찬가지라고 생각하면 된다. 보통 C/C++ 로 개발하다 보면, 편의성이나 type들이 꼬이는걸 막기 위해 개발자들이 별도로 사용하기 위해 type을 재정의하는 경우가 있는데 Objective C도 마찬가지인 듯 보인다. 그냥 쓰기 편하게 unsigned char를 재정의 한 것이고 각각 1을 YES, 0을 NO 라는 키워드로 정의해 두었다. (1바이트 이상의 값을 BOOL 변수에 대입하면 하위 바이트 값만 저장되므로 유의)

며칠전 자기 전에 30분 정도 본 내용인데 정리하려니 시간이 배는 더 걸리는 것 같다. 하지만 이래야 복습이 된다. 간단한 것도 잘 익혀둬야 나중에 삽질하지 않으니 기본은 철저히 익히는게 좋은 듯. 계속 정리할 거다. 꼭...

참! 결과 화면. ㅎㅎ 결과를 봐야 재미 있는게 개발인데...빠져선 안된다.