최초 작성일 : 2011/04/14 20:15 



App name : 전단지 (또는 동네마트)

내용 :

서비스 개요

동네 마트에서 배포되는 세일 전단지(추후 전자제품이나 피자, 치킨 등으로 확대)를

웹으로 입력받아 스마트폰으로 배포하는 서비스


서비스의 효과

- 불필요한 쓰레기를 만들어내는 종이 전단지를 모바일 전단지로 대체하는 효과

- 전단지가 디지털화 됨으로써 전단지 내용을 정보화 할 수 있는 효과


수익모델

- 전체 메인화면 혹은 지역별 메인 화면의 파워링크를 통해 유료 등록한 마트가 가장 앞에

    보여지도록함

- 증강현실 기능, 구매 목록을 미리 정해놓을 수 있는 간이 장바구니 기능, 상품별 가격비교 정보의

    제공 상품별 가격 변동 추이 정보 제공 등을 유료로 서비스


서비스 내용

- (웹)가맹업체(마트)는 전단지에 들어갈 컨텐츠(제품 사진, 가격 정보 등)을 웹사이트를

    이용하여 업로드

- (오프라인)서비스 제공자는 가맹업체들이 업로드한 전단지를 모바일 디바이스에 적절한 형태로

    재편집하여 서비스함

- (웹)가격 정보등의 별도로 DB화 하여 누적시켜 추후 가격 변동 추이 등의 정보제공

    서비스에 이용

- (모바일 + 웹)가맹업체의 위치 정보를 DB화하여 사용자들에게 증강현실 기능을 통한 정보 제공

- (모바일 + 웹)특히 여행이나 출장 등 타 지역에 갔을 때 주변 마트 정보를 활용하기 좋음

- (모바일)모바일 디바이스에서 세일 품목을 선택하여 쇼핑 전에 미리 구매 목록을

    만들 수 있도록 함

- (모바일 + 웹)각 마트별로 가격 비교를 하여 같은 제품을 더 싸게 살 수 있는 곳을 안내

- (모바일 + 웹)누적된 가격 정보를 통해 구체적인 상품에 대한 체감 물가 변동률을

    알 수 있도록 함


적용 기술 : 증강현실, LBS, 기타 웹서비스 관련 기술

확인 사항 :

위험요소

- 전단지 내용이 작은 스마트폰 화면에서도 잘 보이도록 가독성을 높이는 문제

- 사업 초기의 영업 활동을 통한 충분한 전단지 제공처 확보

- 가격 비교등의 서비스를 제공함으로 인해 가맹업체(마트)간의 경쟁 과다 및 이로인한

    가맹업체의 탈퇴문제

- 전단지의 모바일용 편집에 소요되는 인건비의 증가 문제


블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/03/25 16:32 






거의 1년이 다되서 두 번째 업데이트를 했습니다.
큰 변화는 없지만 신경을 좀 썼습니다...^^

변경사항

1. 달력의 절기표시를 수정하였습니다. 절기를 구하는 함수를 추가하여
    절기가 표시되도록 하였습니다. 절기를 구하는 함수는 PHPSCHOOL의 산이님이
    PHP로 만든 소스 중에 절기를 구하는 부분만 Objective-C로 변환하여 사용하였습니다.

2. 일기 작성시 날씨와 현재 위치의 주소값이 자동 표시되도록 하였습니다.
    현재 날씨는 구글 날씨API를 사용중인데 국내 대상 앱이므로 조만간 기상청
    데이터로 변경 예정입니다.

3. 디자인을 변경하였습니다. 레티나 디스플레이를 지원하도록 하였습니다.

소스코드 : https://github.com/woohj70/iPhotoDiary 

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/03/01 00:21 


커스텀 컨트롤 개발 계획의 첫번째로 커스텀 달력을 구현함



현재까지 구현된 기능

1. 토, 일요일 별도 색 표시
2. 언어 설정에 따라 요일명 변경(현재 영어와 한글만 지원)
3. 전달, 다음달 이동 기능
4. '오늘' 표시 기능

개발자가 커스터마이징 가능한 부분

1. 달력에 사용된 이미지 변경
2. 음력 표시 기능
3. 절기 및 국경일 표시 기능

등등...

커스터마이징의 경우 이미 공개된 iPhotoDiary의 소스를 참고하여 수정 가능함
이 달력 소스는 iPhotoDiary에서 사용한 달력을 그대로 가져와 소스만 좀더 단순하게
수정한 것임.

iPhotoDiary 소스코드 : https://github.com/woohj70/iPhotoDiary

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/03/01 00:14


★ 아무리 저질이어도 배경으로 사용된 이미지는 제 창작물이니
저작권을 지켜 주시면 고맙겠습니다...^^






새로 개발 중인 무료 앱 ArtClock에 기본 달력 컨트롤을 구현하였음
더불어 달력 컨트롤을 쉽게 사용할 수 있도록 코드를 수정하여
Mazdah's CustomControl 프로젝트에도 업데이트 함.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/02/22 15:52 







AppStore : iPhotoDiary



사실 네이버의 맥부기 카페에 연재하고 있던 [실전 소스 분석] 마무리 짓고 

소스를 공개하려고 했는데 이래저래 바쁘다보니 연재가 언제 재개될지 기약이 없네요.

그래서 일단 소스를 먼저 공개합니다.


염두에 두실 것은 제가 머리털나고 처음 Objective-C 아이폰을 접하면서 독학으로

만든 것이다보니 소스의 수준이 한참 저질입니다...^^;;;


특히 초보분들은 섣불리  코드 따라 쓰지 마세요...^^;;;

물론 어디에 어떤 용도로 쓰든지 그것은 자유입니다.


그리고 고수분들은 혹시라도 제게 조언을 해주실 것이 있으면

아낌없는 조언 부탁드리겠습니다.


뭐 가장 찾으실만한 내용은 음력을 지원하는 달력이 있는데요

이게 주워온 소스를 사용하다보니 메모리 누수가 보이는 부분이 좀 있네요.

그 부분으 주의 하세요.


https://github.com/woohj70/iPhotoDiary

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/02/15 02:07 


일단 결과부터 말씀드리면

앱 이름은 Day Recorder Pro(유료 $0.99)/Lite(무료)이구요

대상 시장은 전세계입니다.


처음으로 1월 초에 무료버전과 함께 유료버전을 올려봤구요

지금 한달이 조금 넘은 시점에서 다운로드 수는

무료버전이 약 100여건 유료버전이 약31건인데 이중 대부분은 리딤 코드 뿌린 것으로

다운로드 된 것이고 실제 수익은 1월 말까지 정산된 금액이 2먼 2천원 정도네요...^^;;;






일단 이정도면 참담한 실패죠.

실패의 원인에 대한 글은 얼마 전에 이 곳에 올린 적이 있구요.

무조건 1차적인 원인은 앱의 완성도 부족...이라고 말하고 싶어도

아예 다운로드도 안한 상태에서 앱의 완성도를 논할 수는 없다는 점을 생각한다면

다운로드 자체가 안되는 부분은 뭔가 다른 원인이 있을 것 같습니다.

아마도 앱 설명이나 스크린 샷에서 부족한 부분이 많이 있을 것으로 생각되네요.


그리고 얼마전 8일 경에 좀 더 다듬어서 업데이트를 시켰습니다.

버전도 2.0으로 확 높여서(두 번째 업데이트였지만 수정된 부분이 좀 있어서 버전을 확 높였네요) 올렸습니다.


그런데 정작 드리고 싶은 말씀은

결과가 이렇게 참담하다보니 이 결과를 좀 더 아름답게 승화(?)시켜야겠다는 생각이 들더군요.

그렇지 않으면 정말 '실패한 결과'만 남게 될테니까요. 뭔가 그럴싸한 이야기로 실패를 좀 희석시키고싶어지네요...^^;;;


일단 유료 버전을 올리고 나니 엄청난 책임감이 밀려옵니다.

비록 실제 돈을 지불하고 다운로드 받은 사람들이 30여명에 불과하고 또 그 대부분이

생전 본 적도 없고 또 앞으로 볼 일도 없는 외국인이라 할지라도 내가 만든 앱을 받고

실망할 그들을 생각하니 괜시리 미안해지더군요.


허점한 앱 하나 만들어서 유료로 올렸다가 안팔리니까 그냥 내팽개치고 달아나기엔

개발자로서의 양심이나 자존심이 허락을 하지 않네요.


그래서 전략을 바꿨습니다. 시작할 때는 롱테일 법칙에 충실하자고 하여

많은 앱들을 만들어 시장에 깔아놓고자 했는데 사실상 유료 버전에 대한 업데이트 작업만으로도

새로운 앱 개발에 착수하기가 쉽지 않더군요. 결국 알량한 양심과 자존심 때문에

고작 30명(뭐 앞으로는 조금...아주 쬐끔 더 늘어나겠죠...^^)의 고객이긴 하지만

그들을 위해서 보다 완성도 높은 Day Recorder를 다듬어 나가야 겠다는 생각이 듭니다.

그리고 그렇게 하다보면 언젠가는 진흙속에서 보석을 캐내듯 누군가 제 앱의 가치를

알아주지 않을까 하는 조금은 황당한 상상도 해보구요...^^;;;


어쨌든 이번 실패로 저는 다시 직장을 구해야 할 상황이고 조금은 울적한 마음으로

푸념을 좀 늘어놔 봤습니다. 하지만 위에 적은 '양심'과 '자존심'은 한치의 거짓 없는

진심입니다. 이렇게 공개된 자리에 글을 올렸으니 여기 계신 여러분들이 지켜봐주세요.

제 Day Recorder가 변해가는 모습을...^^


아...그리고 마지막으로 이미 모든 분들이 알고 계신 말일테지만 겪고나니 너무나 가슴에

와 닿기에 다시 한번 말씀드립니다.

'화려하고 불완전한 앱보다는 단순하지만 완성도 높은 앱을 만드세요'


그리고 추가로 앱 설명이라든지 스크린샷에 비중을 충분히 두시는 것이 좋으리라 생각됩니다.

그럼 모두들 좋은 앱 만이 만드시고 너무 늦게까지 작업하지 마시고 일찍들 주무세요...^^;;;

전 취업 사이트좀 보다 자야겠네요~^^

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2011/01/11 17:34 





내가 만든 2번째 아이폰 앱인 Day Recorder Pro/Lite가 지난 1월 3일자(미국시간)으로
Ready for sale 되었다.

잘 만들어진 앱은 아니지만 그럭저럭 쓸만한 기능들도 좀 있고 또 이번에는 세계 모든
시장에 등록을 하였기에 그래도 무료버전인 Day Recorder Lite만큼은 하루에 몇백건씩은
다운로드가 될 줄 알았다.

그런데 이게 어찌된 일인가?
앱스토어에 앱이 등록된 지 일주일이 넘은 시점에 전체 다운로드 수는 무료버전인
Lite가 모두 57개, 유료버전인 Pro가 모두 21개(그 중 15개는 프로모션 코드로 받은 것들임...-.-)...

처음 만들어 올린  iPhotoDiary가 무료 버전으로 국내 시장만을 대상으로 하여
그래도 꾸준하게 하루 평균 10개 정도의 다운로드를 기록하고 있는 것에 비하면
뭔가  납득하기 어려운 결과다. 하지만 아무리 눈을 씻고 봐도 이게 현실이었다...ㅠ.ㅠ

상황이 이렇다고 해서 포기해버리기엔 그간 투자한 시간이 너무도 아쉬우니 우선
무엇이 문제인가 분석해보는 것으로 재활용(?)을 시작해보자.

1. 좋지 못한 디자인과 스크린샷

앱을 구매하는 대다수의 고객들은 구질구질한 설명 보다는 앱의 아이콘이나
앱스토어에서 보여지는 스크린샷을 근거로 앱을 구매하는 경우가 많다는 이야기를
본 적이 있다. 이것은 결국 앱의 디자인이 얼마나 중요한지, 그리고 자신의 앱을
잘 표현할 수 있도록 스크린샷을 잘 잡아내는 것이 얼마나 중요한지를 알 수 있게
해주는 사용 행태이다.

결국 본업이 개발자인 내가 직접 한 디자인이 얼마나 형편없었는지가 입증된 것이다...ㅠ.ㅠ

그렇다 할지라도 무료 버전의 경우 단지 호기심에서 받아보는 경우도 적지 않을터,
그런 면을 감안하더라도 무료버전 다운로드 횟수는 너무도 적은 수치이다.
그렇다면 다음의 이유 때문일까?

2. 어설픈 국제화

처음 iPhotoDiary를 만들어 올리고는 하루 평균 10건 정도밖에 안되는 다운로드 수에
엄청 좌절했었다. 그래서 고민끝에 내린 결론은 역시 세계 시장에 도전을 해야 한다는
것이었다. 하지만 그 과정에서 지역화를 고려하지 않아 정작 국내 사용자들에게
한글로된 설명과 앱을 제공하지 못한 것이 문제 중의 하나였다.

더욱 안좋은 것은
영어로된 설명 조차 검증받지 못한 상태로 작성된 것이라 영어권 사용자들에게조차
그 의미가 제대로 전달이 된 것인지도 확실치 않다는 것이다.

결국 전문적이지 못한 국제화 작업이 사실상 세계 어느 곳에서도 의미가 통하지 않는
이상한 설명문을 만들어냈고 이것이 사용자들로 하여금 다운로드 받는 것을 주저하게
만든 것이 아닐까 하는 생각이다.

게다가 텍스트가 의미가 통하지 않는다면 스크린샷 같은 이미지로 감을 잡아야 하는데
이조차 1번에서 설명한 바와 같은 문제를 안고 있다보니 역시너지(?)를 만들어 낸 것 같다.

3. 비인기 카테고리의 선택

Day Recorder Pro/Lite는 메인 카테고리가 라이프 스타일이고 서브 카테고리가
여행이다.

앱스토어에 등록된지 일주일이 지난 현재 Pro버전은 오늘(11일) 업데이트가 되어
카테고리의 첫 페이지에 있고 Lite 버전도 아직 두 번째 페이지에 있다.

그만큼 사용자도 적고 그래서 등록되는 앱의 수도 적다는 말이다.
내 앱이 가지고 있는 가치를 보여줄 기회조차 제대로 얻을 수 없었던 것은 아닐까?








4. 프로모션의 부재.

메인 시장을 미국으로 잡은 주제에 실제로 프로모션 코드는 내가 활동하고 있는
카페에 대부분 배포하였다... 이게 뭔 삽질인지...-.-

외국의 유명한 아이폰 앱 관련 블로그에 프로모션을 해야 하겠지만 2번에서도
언급했듯이 언어의 장벽이 만만치 않다...ㅠ.ㅠ

5. 앱의 완성도 부족

가장 중요한 내용이네요. 완성도가 부족한 앱은 무얼 해도 안팔리겠죠...-.-

일단은 짐작할 수 있는 4개의 이유를 적어보았다. 물론 어느 하나 확실한 근거가
있는 것은 아니다. 단지 심증일 뿐.

하지만 이렇게까지 처참한 결과가 나온 데에는 반드시 이유가 있을 것이고 그 것을
밝혀내야 내가 3개월을 고생해서 만든 앱을 그나마도 쓸모있게 만드는 일일 것이다.

아울러 아직도 포기하고 싶지 않은 이유는 Long tail의 법칙을 믿기 때문이다.
비록 지금은 이렇게 누구의 눈에도 띄지 않고 사장되어 있지만 어차피 앱스토어에
등록되어있는 동안 비용이 들어가는 것도 아니고 취미 삼아 조금씩 조금씩 개선을하고
업데이트를 해 나가면 언젠가는 좋은 앱이 되고 많은 사람들에게 알려질 수 있지 않을까 하는
조금은 야무진 꿈을 꿔본다...^^;;;

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2010/12/24 02:10 


사유는 백그라운드 실행시 위치 정보 업데이트 시키는 부분과 관련된 문제인데요.

제가 어딜 잘못했는지 확인하는데만도 시간이 좀 걸릴 것 같고 그리 중요한 기능도 아니고 해서
그냥 백그라운드 위치 정보 사용 관련 부분을 info.plist에서 제외시키고 다시 올렸습니다.

이번엔 통과해야 할텐데 걱정이네요...ㅠ.ㅠ
아래는 메일 전문입니다.

Thank you for submitting Day Recorder Pro to the App Store.
 
We've completed the review of your app, however, we cannot post this version to the App Store because it does not provide any functionality that requires persistent location, but it uses the location background mode, which is not in compliance with the App Store Review Guidelines <https://developer.apple.com/appstore/resources/approval/guidelines.html>:
 
 2.16 Apps may only use background multitasking for one of the approved background modes; VoIP, audio playback, location, task completion
We have included additional details below to help explain the issue and hope you’ll consider revising and resubmitting your application.
 
Your app has declared support for location in the UIBackgroundModes key in your Info.plist. You'll need to add features that require location updates while the app is in the background or remove the "location" setting from the UIBackgroundModes key. 
 
You may wish to refer to the section "Executing Code in the Background" found in the iOS Reference Library <http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html>.
 
In addition, your application is using Location Background Mode, you will need to include a battery use disclaimer in your marketing text. Please add the following notice to your Application Description in iTunes Connect, "Continued use of GPS running in the background can dramatically decrease battery life."
 
While your application has been approved, please be sure to update the application description as soon as possible to avoid any interruption in the availability of your app on the App Store. 
 
For future app submissions, please be sure to review the App Store Review Guidelines Guidelines <https://developer.apple.com/appstore/resources/approval/guidelines.html> to help ensure a successful app review.
 
If you have any questions about this response, or would like to discuss it further, please feel free to reply to this email. If you would like to appeal this review, please submit a request to the App Review Board at <http://developer.apple.com/appstore/resources/approval/contact.html>.

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2010/11/04 02:50 



<bar code, QR code>
 
<push notification service 관련 provider>

<web 게시판과 연동>

http://cocoadev.tistory.com/#recentTrackback에서 공개한 내용임.
      *  이미지 편집 함수 모음( 스케일, 회전, crop 등)
  • 다양한 UI 구현
  • 테이블뷰셀 커스터마이징
  • HTTP GET/POST 요청
  • XML 파싱
  • 사진 앨범, 카메라, 지도 이미지 접근
  • 맵뷰 및 위치정보
  • 푸시 노티피케이션

<여러 UI 모음: photo viewer, etc>
http://github.com/facebook/three20 (초기에는 facebook 어플이었으나 현재는 여러 UI 모음으로 바뀜 )

<map>

<E-mail>
 
 
<달력>
http://ved-dimensions.blogspot.com/2009/04/iphone-development-creating-native_09.html

<sqlite>

<계산기>

<트위터 클라이언트>
http://github.com/blog/329-natsuliphone-iphone-twitter-client
http://code.google.com/p/tweetero/

<facebook>

<rss reader>
http://code.google.com/p/iphone-simple-rss-aggregator/

<ebook reader>
http://code.google.com/p/iphoneebooks/

<blog>
http://iphone.wordpress.org/

<백업, 동기화>
http://www.funambol.com/solutions/iphone.php
http://code.google.com/p/gris/ (구글 리더 동기화)

<time tracking>
http://github.com/freshbooks-addons/freshbooks-iphone-project

<게임>
http://code.google.com/p/cocos2d-iphone/
http://code.google.com/p/tris/ (테트리스)
http://code.google.com/p/mintgostop/ (고스톱)

 

<google toolbox>

http://code.google.com/p/google-toolbox-for-mac/


<택배>

 

<이미지 프로세싱>

http://code.google.com/p/simple-iphone-image-processing/


<증강현실>
http://www.iphonear.org/

<coverflow 대체 구현>
http://apparentlogic.com/openflow/
http://www.chaosinmotion.com/flowcover.m (매가박스 어플에서 참고함)

<정규표현식 라이브러리>
http://blog.mro.name/2009/09/cocoa-wrapped-regexh/
http://regexkit.sourceforge.net/RegexKitLite/

<라이브러리 : JSON, DOM XML, Google Data APIs, Twitter, Flick, Game Engines, Unit Testr>
http://www.codingventures.com/2008/12/useful-open-source-libraries-for-iphone-development/

<기타>
http://open.iphonedev.com/
http://joehewitt.com/post/the-three20-project/

블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^

최초 작성일 : 2010/10/11 00:58 


1. 네비게이션 아이템을 통한 화면의 이동


오늘은 잡설 없이 바로 본론으로 들어갑니다…^^;;;


지난 시간 보았던 메인 화면 상단의 navigation controller에는 좌측과 우측에 각각 버튼이 하나씩

있습니다. 좌측의 버튼은 등록된 아이들의 리스트를 보고 수정 및 삭제를 할 수 있는 화면으로

이동하는 버튼이고 우측의 버튼은 새로 아이를 등록하는 화면으로 이동하는 버튼입니다.


네비게이션 컨트롤러에 별다른 커스터마이징을 하지 않았다면 대체로 이 버튼들은

UIBarButtonITem의 인스턴스들이고 이 인스턴스를 생성하는 메서드들에는 이벤트와

이벤트 발생시 수행되는 메서드(@selector로 지정되는)를 설정할 수 있도록 되어있습니다.

(다만 기본 생성 메서드인 initWithBarButtonSystemItem에는 이벤트는 별도로 지정하지

않고 selector만 지정합니다.)


iPhotoDiary의 메인 화면에서는 다음과 같이 2개의 버튼을 붙였습니다.


UIBarButtonItem *addButton = [[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemAdd

target:self 

action:@selector(addChild)];

self.navigationItem.rightBarButtonItem = addButton;

[addButton release];

UIBarButtonItem *editButton = [[UIBarButtonItem alloc]

initWithBarButtonSystemItem:UIBarButtonSystemItemEdit 

target:self 

action:@selector(editChild)];

self.navigationItem.leftBarButtonItem = editButton;

[editButton release];


그리고 각 버튼의 selector들은 다음과 같습니다.


- (void)addChild {

AddNewChildViewController *nextViewController =

[[AddNewChildViewController alloc

 initWithManagedObjectContext:managedObjectContext];

nextViewController.hidesBottomBarWhenPushed = YES;

[self.navigationController pushViewController:nextViewController

   animated:YES];

}


- (void)editChild {

childList = [[ChildListViewController alloc]

 initWithManagedObjectContext:self.managedObjectContext];

childList.hidesBottomBarWhenPushed = YES;

[self.navigationController pushViewController:childList 

   animated:YES];

}


너무도 평범한 내용이라서 굳이 글로 옮기는 것 자체가 낭비네요…^^;;;

다만 초반에도 제가 말씀드린대로. 네비게이션 컨트롤러의 구성을 잘 못한 경우 네비게이션 컨트롤러의

기본적인 기능은 동작하지만 childList.hidesBottomBarWhenPushed = YES;이 문장이

적용되지 않는 경우가 있습니다. 다른 방법으로 하단의 탭바를 감출 수는 있지만 작업이 복잡해지죠.

탭바의 숨김과 보여짐을 좀더 쉽게 처리하기 위해서는 IB를 통해 네비게이션 컨트롤러를 구성하는 것이

좀 더 쉬운 방법이라 판단됩니다.

참고 링크 : [실전 소스 분석] 2. 프로젝트 생성과 메인 화면 구성


또, 이미 잘 알고 계시는 바와 같이 네비게이션 컨트롤러를 통해 움직이는 뷰들은 계층 구조를

가지고 있으며 이 것은 메모리 구조상의 stack 형태를 가지고 있습니다. 즉 가장 나중에

push된 것이 가장 상위에서 사용자에게 보여지게 되는 것입니다. 그리고 가장 나중에

push된 것부터 먼저 pop되어 stack에서 빠져나가면 그 바로 아래에 있던 view가 사용자게에

보여지게 됩니다. 메서드명에 push와 pop이라는 단어가 들어간 것으로도 이미 짐작을

하셨을 것입니다.


이렇게 해서 메인 화면에 보여질 데이터를 등록하는 화면과 등록된 데이터를 수정/삭제할 수 있는

화면으로 이동이 가능하게 되었습니다.


나의 실수


처음에 뷰 컨트롤러의 인스턴스를 생성하여 그 인스턴스를 pushViewController에 사용하였는데

자꾸 에러가 발생하는 것이었습니다. 나중에 알고보니 습관적으로 뷰 컨트롤러의 init 메서드를

사용하는데 기본 init메서드인 initWithNibName:…를 사용해서 문제가 된 것이었습니다.


IB에 대해서도 앞서 언급을 했지만 간혹 IB를 통해 xib 파일을 만들었다가 굳이 xib가 필요치 않아

삭제를 하거나 혹은 디자인 설계 용도로만 사용하고 삭제를 하는 경우가 있었습니다.

이 때 xib 파일이 있으니까 initWithNibName:…메서드를 통해 뷰 컨트롤러의 인스턴스를

만들었다가 xib 파일을 지우고나니 이 xib 파일을 찾지 못해  initWithNibName:…에서 에러를

낸 것이었습니다.


저는 초짜라 그런지 꽤 자주 겪은 실수였습니다…^^;;;



2. 테이블 뷰 컨트롤러의 사용


대부분의 애플리케이션에서 설정 화면이나 로그인 또는 등록 화면에 테이블 뷰(컨트롤러)를 많이

사용합니다. 아마도 테이블 뷰가 입력 항목이 많아졌을 때 아래 목록까지 스크롤을 시킨다든가

하는 부분에서 수고를 덜 수 있고 또 비슷한 입력 항목끼리 그룹화하기도 편하기 때문이 아닌가

싶습니다.


저같은 경우 사실은 입력 항목이 많지 않아 스크롤 될 필요도 없고 또 오히려 테이블 뷰를

사용하는 과정에서 문제가 발생하여 굳이 테이블 뷰를 사용하지 않아도 될뻔했습니다.

거의 '테이블 뷰 컨트롤러에 대해 공부하겠다'는 생각만으로 테이블 뷰 컨트롤러를 선택하게

되었습니다…^^;;;


일단 화면 구성은 아래와 같습니다.






그룹 타입의 테이블이고 3개의 그룹으로 구성되어 있으며 첫 그룹은 1개의 커스텀 셀로 되어

있습니다.


여기까지만 봐도 커스텀 셀을 만드는 방법, 또 2개의 셀에는 segment 컨트롤이 들어있는데

이렇게 셀에 컨트롤을 넣는 방법, 그리고 이 화면은 나중에 수정 화면으로도 같이 사용되는데

그랬을 경우 Core Data와 연계하여 사용하는 방법, 테이블 뷰에 배경 이미지 넣는 방법 등

할 이야기가 많지만 이 내용들은 다음 절로 넘기고 여기서는 테이블 자체를 만드는 내용만

살펴보도록 하겠습니다.


먼저 테이블 뷰 컨트롤러를 바로 만들었을 경우에는 기본적으로 구현되어있는 메서드들이

몇가지 있습니다. 먼저 다음 3개의 메서드가 있습니다.


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

- (NSInteger)tableView:(UITableView *)tableView 

numberOfRowsInSection:(NSInteger)section

- (UITableViewCell *)tableView:(UITableView *)tableView 

cellForRowAtIndexPath:(NSIndexPath *)indexPath


그리고 이 메서드들이 선언된 앞쪽에는  pragma mark에 Table view data source라고

적혀있습니다. 네 바로 데이터와 연동되어 테이블의 구조를 만들어내는 메서드들입니다.

물론 데이터는 코어 데이터일 수도 있고, 단순 배열일 수도 있고, Dictionary일 수도 있고

그렇습니다.


각각 차례대로


- 테이블의 섹션 갯수를 설정하는 메서드

- 각 섹션의 Row 수를 가져오는 메서드

- 각 Row의 TableViewCell을 설정하는 메서드입니다.


이번에 다루고 있는 화면 같은 경우 동적으로 변하는 구조가 아니라 섹션과 Row 수가

등록할 입력 값의 수로 고정되어있는 화면이라서 몇몇 곳에서는 리턴값으로 바로

정수형 상수를 사용하고 있습니다.


예를 들어 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

경우에는 return 3;이 코드의 전부입니다.


물론 numberOfRowsInSection메서드의 경우에도 3개의 섹견 각각에 몇개의 Row가 필요한지

이미 결정되어있는 상태라 if문을 통해 두번째 인자로 넘어오는 section 값을 확인하여

각 섹션에 맞는 Row 수를 상수로 리턴하고 있습니다.


// Return the number of rows in the section.

    NSInteger rows = 0;

    

    /*

     The number of rows depends on the section.

     In the case of ingredients, if editing, add a row in editing mode to present an "Add Ingredient" cell.

 */

    switch (section) {

        case BASIC_SECTION:

rows = 1;

break;

        case BODY_SECTION:

            rows = 2;

            break;

        case BORN_SECTION:

rows = 3;


          break;

default:

            break;

    }

    return rows;


여기에서 대문자로 적혀있는 것들은 매크로로 지정해놓은 section값입니다. 차례대로 0,1,2입니다.


나의 후회


물론 저는 처음이고 공부를 하면서 개발을 하던 상태였기에 이렇게 코딩을 하였지만

나중에 이 코드를 재사용한다는 측면에서도 상수를 바로 사용하는 것은 좋지 못한 습관이겠죠^^;;


가급적이면 변수를 사용하여 나중에 이 코드에 동적인 데이터를 사용하더라도 수정하게되는

범위가 줄어들 수 있도록 하는 것이 좋을 것입니다.


여기까지는 별게 없는데요…마지막  cellForRowAtIndexPath 메서드가 많이 복잡해졌습니다.

일단 각 섹션별로 Row의 수가 틀리고 첫번째 섹션과 나머지 섹션이 각각 서로 다른

커스텀 셀들을 사용하고 있습니다. 그리고 각각의 타이틀이 틀리고 어떤 셀에는 컨트롤이

포함되어있습니다. 어떤 식으로 구성하였는지 소스에 주석을 다는 형식으로 살펴보도록

하겠습니다.


일단 기본적인 커스텀 셀은 2가지입니다. ChildBasicInfoCell과 ChildInfoCell인데요

이 때까지만 해도 제가 테이블 셀에 컨트롤을 넣는 방법을 제대로 몰라서  segment 컨트롤이

들어가 별도의 커스텀 셀을 하나 더 만들었습니다. ChildGenderInfoCell이 그것입니다.


이 메서드이 내용은 이렇게 3가지 형태를 갖고 있는 커스텀 셀들을 적절한 위치에 배치시키는

것입니다.


if (indexPath.section == BASIC_SECTION) {

//먼저 첫번째 섹션의 경우 처리입니다.

static NSString *CellIdentifier = @"ChildBasicInfo";

    

//UITableViewCell이 아니라 커스텀 셀의 인스턴스를 만듭니다.

ChildBasicInfoCell *cell = (ChildBasicInfoCell *)[tableView 

dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) { //만일 재사용할 셀이 없을 경우의 처리죠…

//이 부분부터 특이해지는데요 메인 번들을 통해 Nib 파일 이름을 통해 커스텀 셀들의

   배열을 가지고옵니다.

NSArray *nib = [[NSBundle mainBundle]

loadNibNamed:@"ChildBasicInfoCell" 

owner:self 

options:nil];

//가지고 온 커스텀 셀들의 배열을 for문을 돌려서...

for (id oneObject in nib) {

//이 섹션에서 사용할 커스텀 셀과 같은 클래스이면…작업을 합니다.

if ([oneObject isKindOfClass:[ChildBasicInfoCell class]]) {

cell = (ChildBasicInfoCell *) oneObject;

cell.delegate = self;

//이 if문은 이 화면이 수정 화면으로 쓰일 경우의 처리입니다.

                                   //기존 데이터를 해당 셀에 대입하는 것이죠.

if (childData != nil) {

cell.nameField.text = childData.childname;

cell.birthDay.text =

[self.dateFormatter 

stringFromDate:childData.birthday];

cell.birthDate = childData.birthday;

cell.isLunar.selectedSegmentIndex = [[childData 

valueForKey:@"islunar"integerValue];

CGSize size = childData.thumbnailImage.size;


CGFloat ratio = 0;


//화면에 보여줄 이미지의 크기를 조절합니다.

if (size.width > size.height) {

ratio = 116.0 / size.width;

else {

ratio = 116.0 / size.height;

}

CGRect rect =

CGRectMake(0.0,

   0.0,

   ratio * size.width,

   ratio * size.height);

UIGraphicsBeginImageContext(rect.size);

[childData.thumbnailImage drawInRect:rect];

[cell.photoViewButton 

setImage:

UIGraphicsGetImageFromCurrentImageContext()

forState:

UIControlStateNormal];

UIGraphicsEndImageContext();

}

childBasicInfoCell = cell;

}

}

}

return cell;

else { //섹션이 두번째나 세번째인 경우

//세번째 섹션의 2번째 Row의 경우 segment 컨트롤을 넣기 위해 별도의 커스텀 셀로

  //만들었습니다,

if (indexPath.section == BORN_SECTION && indexPath.row == 2) {

static NSString *CellIdentifier = @"childGenderInfo";

//성별을 입력하는 부분은 segment 컨트롤을 사용하고 있어 별도의 커스텀 셀을

//만들었습니다.

ChildGenderInfoCell *cell = (ChildGenderInfoCell *)[tableView

dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) { //역시 재사용할 셀이 없는 경우...

NSArray *nib = [[NSBundle mainBundle]

loadNibNamed:@"ChildGenderInfoCell" 

owner:self 

options:nil];

for (id oneObject in nib) {

//사용할 커스텀 셀과 같은 클래스인 경우

if ([oneObject

 isKindOfClass:[ChildGenderInfoCell class]]) {

cell = (ChildGenderInfoCell *) oneObject;

cell.delegate = self;

cell.labelField.text = @"";

}

}

}

//셀 타이틀을 지정합니다.

cell.labelField.text = [self.cellTitle2 

objectAtIndex:indexPath.row];

//역시 수정 화면으로 사용될 경우 값을 대입하게 됩니다.

if (childData != nil) {

cell.genderControl.selectedSegmentIndex =

[[childData valueForKey:@"gender"integerValue];

}

return cell;

else {

//두번째 섹션 또는 세번째 섹션은 Row 1, 2인 경우

static NSString *CellIdentifier = @"ChildInfo";

ChildInfoCell *cell = (ChildInfoCell *)[tableView 

dequeueReusableCellWithIdentifier:CellIdentifier];

//ChildInfoCell은 타이틀을 위한 Label 하나와 값을 입력할 TextField 하나로

            //구성된 커스텀 셀입니다.

if (cell == nil) {

NSArray *nib = [[NSBundle mainBundle]

loadNibNamed:@"ChildInfoCell" 

owner:self 

options:nil];

for (NSInteger i = 0; i < [nib count]; i++) {

id oneObject = [nib objectAtIndex:i];

if ([oneObject isKindOfClass:[ChildInfoCell class]]) {

Debug(@"cell = ChildInfoCell");

cell = (ChildInfoCell *) oneObject;

cell.delegate = self;

cell.labelField.text = @"";

cell.valueField.text = @"";

}

}

}

//각각의 셀에 타이틀을 설정합니다. 타이틀은 cellTitle1과 cellTitle2라는

//배열에 저장되어있습니다.

//

//self.cellTitle1 = (NSArray *)[NSArray arrayWithObjects:

// @" : ", @"몸무게 : ", nil];

//self.cellTitle2 = (NSArray *)[NSArray arrayWithObjects:

// @"주민번호 : ", @"출생지 : ",

// @"성별 : ", nil];

if (indexPath.section == BODY_SECTION) {

cell.labelField.text = [self.cellTitle1 

objectAtIndex:indexPath.row];

else {

cell.labelField.text = [self.cellTitle2 

objectAtIndex:indexPath.row];

}

//수정 화면으로 사용될 경우 Core Data에서 불러온 데이터를 설정하게 됩니다.

if (childData != nil) {

if (indexPath.section == BODY_SECTION) {

if (indexPath.row == 0) {

cell.valueField.text = [childData.height 

stringValue];

else if (indexPath.row == 1) {

cell.valueField.text = [childData.weight 

stringValue];

}

else {

if (indexPath.row == 0) {

cell.valueField.text = childData.idnum;

else if (indexPath.row == 1) {

cell.valueField.text = childData.location;

}

}

}


//마지막으로 숫자만 입력받을 셀에 대해서는 키보드 타입을 따로 설정해주었습니다.

if (indexPath.section == BODY_SECTION ||

(indexPath.section == BORN_SECTION && indexPath.row == 0)) {

cell.valueField.keyboardType =

UIKeyboardTypeNumbersAndPunctuation;

else {

cell.valueField.keyboardType = UIKeyboardTypeDefault;

}


return cell;

}


}


이상 실제 소스를 살펴보았지만 번잡스럽게 커스텀 셀이 3종류나 되어 실제 Row 수가 6개인 것을

감안하면 코드가 필요이상으로 복잡해졌습니다. 조금 만 더 다듬으면 코드는 더 짧아질 것입니다.


해결하지 못한 문제점…ㅠ.ㅠ


일단 이렇게 구성을 하고 테이블 뷰가 스크롤 가능하도록 처리를 했습니다.

그런데 좀 이상한 문제가 발생을 하였습니다.


스크롤을 몇 번 하고나면 셀의 위치가 바뀌어 있는 것입니다.

예를들어 섹션 1의 0번째 Row가 주민번호인데 이것이 섹션 2의 2번째 Row에 표시되는

그런 문제였습니다.


우선 데이터 소스 관련 3개의 메서드는 스크롤이 발생할 때마다 계속 수행이 되는 것을 확인해본 바

일단 심증으로는 셀을 계속 재사용하는 과정에서 셀을 불러올 때 처음 정해진 순서대로

셀이 반환지 않는 것이 아닐까 하는 점입니다.


어쨌든 이 부분은 아직도 해결을 하지 못해서 일단 테이블을 스크롤이 되지 않도록

고정시켜놓은 상태입니다. 혹시 제 소스 코드를 보시고 문제가 있을법한 부분이 있다면

수정 부탁드립니다…^^;;;


이번 주는 유독 피곤하기도 하고 또 신규로 개발 중인 앱의 진행이 원활하지 못해서 일단

이번 주는 여기서 글을 맺을까 합니다.

길고 평범한 글 읽어주셔서 감사합니다. 다음 주에 아래 내용으로 다시 찾아뵙겠습니다.


3. 커스텀 셀

4. 셀에 컨트롤 넣기

5. 테이블 뷰의 배경

6. 등록과 수정 화면 공유

7. 테이블 뷰의 스크롤

8. 요약

9. 마무리


블로그 이미지

마즈다

이미 마흔을 넘어섰지만 아직도 꿈을 좇고 있습니다. 그래서 그 꿈에 다가가기 위한 단편들을 하나 둘 씩 모아가고 있지요. 이 곳에 그 단편들이 모일 겁니다...^^