미생의 다리

Photography/Space 2015. 3. 7. 18:00

방산대교 앞, 시흥 갯골 남북로 기지개를 켜고 있는 다리가 있다. 시흥시의 캐치프레이즈인 ‘미래를 키우는 생명의 도시’의 첫 글자를 빌려 “미생의 다리”로 알려졌는데, 다리의 이름은 아직 정해지지 않았다고 한다. 하지만 이미 많은 사람의 입에 오르내린 이름을 대체하기란 쉽지 않아 보인다.


이곳은 목섬에서 갈매기를 만나고 집으로 돌아오는 길에 우연히 들리게 되었다. 출사지를 인터넷으로 검색할 때 스쳐 지나갔던 다리가 보여 무작정 차를 세웠다. 서해안로를 따라 신천동 방향으로 가다 보면 방산대교를 만나게 되는데, 방산대교를 진입하기 전에 오른쪽 샛길로 내려가면 미생의 다리까지 걸어서 진입하는 길을 만날 수 있다.


나는 사진을 찍을 때, 몰려다니거나 포인트라고 하는 장소에서 사진 찍는 것을 별로 좋아하지 않는다. 물론 같은 장소, 같은 화각이라도 담는 사람에 따라 다른 느낌을 전달 할 수 있다지만, 멋진 장소에서 멋들어진 사진을 담지 못한다고 해도 나만의 시각에서 내 이야기를 담는 게 좋다. 유독 우리나라 사람은 유행이나 이슈에 민감한 것 같다. 사진 촬영도 예외는 아니다. 하지만 오늘은 나 또한 예외가 아니다.

















    

설정

트랙백

댓글

영흥면 목섬 갈매기

Photography/Space 2015. 3. 7. 16:00

흔히 바다를 떠올리면 흰 모래사장과 푸른 하늘과 맞닿은 바다, 동해를 생각한다. 동해는 깨끗함과 바다 향이 있는 반면, 서해는 갯벌 냄새와 가공하지 않은 날것의 느낌이 있다. 환경은 다르지만, 서해에도 어김없이 갈매기가 있다. 바다 풍경과 더불어 한가로운 정서를 느끼게 한다. 그래서 옛시조에 갈매기가 많이 등장하는지도 모르겠다.


갈매기는 전 세계에 약 86종이 알려졌는데 우리나라에는 13종이 있는 것으로 기록되어 있다고 한다. 그중에서도 흔히 볼 수 있는 텃새 갈매기는 황새목(도요목) 갈매깃과에 속하는 괭이갈매기다.


괭이갈매기는 몸길이 약 46cm, 날개 길이 34~39cm의 중형 갈매기로, 머리와 가슴·배는 흰색이고 날개와 등은 잿빛이다. 고양이 울음소리와 비슷하다고 하여 괭이갈매기라는 이름을 붙였다고 하는데, 내 생각에는 무리생활을 하는 것과 강한 귀소성이 영역을 중시하는 길고양이의 삶과 비슷하기 때문이 아닐까 싶다. 턱시도 고양이가 괭이갈매기의 깃털을 핥고 있어도 전혀 어색하지 않을 것 같다.














사진을 찍다 보니 유난히 카메라를 의식하는 갈매기가 있다. 사진 몇 컷 찍게 해주면 새우깡을 주겠지 하는 표정으로 요리조리 자세를 취한다. 우리나라에서 갈매기에게 먹이를 주기 위해 한 해에 판매되는 새우깡은 몇 봉지나 될까, 또 팝콘과 새우깡 중에 어느 것이 더 맛있다고 할까, 허니버터칩에 대한 평가는 어떨지도 문득 궁금하다.








    

설정

트랙백

댓글

강화도 마니산[摩尼山]

Photography/Space 2015. 3. 1. 14:40

헤이리에서 일정이 어긋나서 평소에 생각해두던 강화도 마니산(높이 472.1m)에 다녀왔다. 마니산은 단군왕검이 하늘에 제사를 지내기 위해 마련했다는 참성단(塹城壇:사적 136)이 있다. 지금도 이곳에서 개천절에는 제례를 올리고, 전국체육대회의 성화가 채화된다고 한다. 


나는 상방리 매표소에서 계단로(4.8㎞/소요시간 2시간)를 따라 참성단에 올랐다. 계단로는 높은 경사면에 계단식으로 길이 나 있어 많은 관광객이 자주 이용하는 코스다. 산세가 험하지 않아 가족 단위의 관광객을 많이 볼 수 있었는데 평소에 걷거나 운동을 좋아하지 않는 사람은 다음날 조금 고생할 수도 있겠다.


날씨가 따라주지 못하여 서해의 먼 지평선까지 볼 수는 없었지만, 한나절 천천히 올라가 볼 만한 곳이다.
























식당에서 만난 녀석이 보나 자마 나에게 눈웃음을 친다. 꼬리를 보니 길고양이 같은데 식당에서 돌보는지 목줄에 화장실까지 있다. 사랑을 받는 녀석이 좋아 보이기도 하지만 다른 한편으로는 답답할 거 같기도 하다. 어쨌든 슬퍼 보이지는 않아 다행이다.







    

설정

트랙백

댓글

세월의 흔적...

Photography/Snapshot 2015. 3. 1. 10:30

특별한 일 없이 지나가는 주말의 아쉬움이 점점 커지는 것 같다. 일요일을 어떻게 보낼까 궁리하다가 헤이리에 있는 「고막원」 카페에 방문하기로 했다. 가서 책도 보고 사진도 찍을 요량으로 손님에게 방해되지 않는 오픈 시간에 맞춰서 방문했지만, 카페 사장님께서 사진 찍는 것을 허락하지 않는다. 그 사연이 궁금하여 물어보아도 그냥 좋아하지 않는다고 하니 아쉽지만 어쩔 수 없이 발길을 돌려야 했다. 남자 혼자 오픈 시간에 사진을 찍는다고 하니 사장님 눈에는 불편한 업자로 비친 모양이다. 헤이리를 한 바퀴 돌며 몇 장의 사진을 찍었다. 주말인데도 사람을 찾아보기가 힘들다. 어찌 된 일인지 요즘 헤이리 분위기가 예사롭지 않다.











    

설정

트랙백

댓글

개발자의 작은 기록...

Project/Mobile 2015. 2. 27. 22:03


https://itunes.apple.com/us/app/pics2mov/id788398033?mt=8


2012년 말 회사를 나와서 만 2년 동안 개인 프로젝트에 온 힘을 다했다. 가시적인 결과는 그리 풍성하지는 못하지만 움켜쥐고 있던 것을 잃어가며 배울 수 있었던 점은 감사하게 생각한다. 경험하지 않은 것에 대해 막연한 두려움은 누구나 가지고 있다. 나 또한 그랬다. 누구에게도 들키고 싶지 않았다. 그래서 많은 것을 잃으면서도 초연하려 노력했다. 내 노력의 동력이 무엇인지는 모르겠으나 적어도 선택할 수 없는 환경에서 주어지는 어쩔 수 없는 결과였다고는 생각하지 않는다. 그래야 지금의 나의 만족도 성립할 테니 말이다.


사실 누구나 그렇겠지만, 개인 개발자는 단 하나의 프로젝트로 성과 내기를 기대한다. 돌이켜보면 나 또한 그랬다. 하지만 현실은 냉혹하게도 마냥 이상만을 꿈꾸며 나아갈 수가 없다. 세 개의 프로젝트를 진행하면서 매번 나에게 질문한 것은 ‘나는 발전하고 있는가’였다. 수많은 선택을 통해서 발전을 하고 있는가는 크고 작음을 떠나서 나에게 가장 중요한 질문이었다. 그것은 항상 고민이었고 또한 큰 의문이었다. 


사실, 문제를 문제로 인식하지 못하는 상황, 밖에서 프레임 안을 내려다보지 못하는 상황에서는 이러한 질문이 무의미할 수도 있다. 그것을 인정할 용기가 필요했을지도 모르겠다. 과거의 프레임을 돌아보는 지금의 상황도, 어쩌면 내 인생의 또 다른 프레임일 것이다. 나는 언제나 그 프레임을 깨고 밖에서 바라보는 나를 희망한다. 어떻게 보면 이것이 내가 생각하는 「발전」 일지도 모른다. 


그러니 나는 또 다시 시작할 수밖에 없다.


    

설정

트랙백

댓글

오랜만에 찾은 가배나루...

Photography/Snapshot 2015. 2. 25. 21:59

예전 회사에서 가끔 찾던 곳 가배나루... 집으로 돌아오는 길에 오랜 만에 나루 고양이들이 잘 있나 궁금도 하고 책이나 볼 요량으로 찾았으나... 비 오는 날이 장날이라고 내부 인테리어 공사가 한창이었다. 요즘 카페 고양이들을 몸소 찾아뵙기가 어렵다. 그래 너희도 다음에 보자.







    

설정

트랙백

댓글

고양이하라...

Photography/Memorials 2015. 2. 24. 21:14

어쩌면 책의 내용을 탐하기보다는 이용한 시인에 대한 믿음이 더 크게 작용하였는지도 모르겠다. “흐리고 가끔 고양이"와 "여행하고 사랑하고 고양이하라”는 여행과 동물 좋아하는 나에게 흥미를 끌 만 했는데, 이 두 권의 책을 읽으니 작가가 길고양이에게 미친(?!) 사연이 궁금하기도 하고 내가 이 책 한 권을 구매하면 길고양이들의 입으로 들어갈 사료와 간식은 얼마나 될까 하는 엉뚱한 계산도 하게 된다. 누구에게는 이상한 시인의 미친 사랑으로 보일지는 몰라도 나는 이용한 시인과 그 주변의 모든 사람을 응원한다. 이 땅에 태어난 모든 길고양이에게 축복이 함께하기를 기원하며…




    

설정

트랙백

댓글

비 내리는 개미마을...

Photography/Space 2015. 2. 22. 01:12

서울 서대문구 홍제동 인왕산 아래에 자리 잡고 있는 서민층 거주마을이다. 이곳이 처음 생성된 시기는 1950년 6·25 전쟁 당시 빈자들이 이곳에 자리를 잡게 되면서 생성되었다고 한다. 인디언 시절의 모습과 비슷하다고 하여 붙여진 이름이 인디언타운이었으나 개미처럼 부지런하게 사는 마을이라는 뜻으로 개미마을이라고 이름을 바꾸게 되었다고 한다. 특이한 점은 가구 간에 화장실이 없는 탓에 공동화장실에서 용변을 보는 풍습이 있다고 한다. 개미마을은 영화 ‘7번 방의 선물’의 배경이 되기도 했다. 홍제역 1번 출구에서 7번 마을버스를 타면 마을의 끝자락 종점까지 갈 수 있는데 영화 7번 방의 선물에서 7은 이 마을버스에서 착안한 것이 아닌지 모르겠다.






























    

설정

트랙백

댓글

그래도 좋다.

Photography/Snapshot 2015. 2. 15. 22:45

삼청동 길목, 이름 모를 뮤지션






    

설정

트랙백

댓글

어쩌면사무소...

Photography/Space 2015. 2. 15. 17:00

오늘은 작정하고 나왔건만... 당분간 일요일에는 면장 고양이가 출근하지 않는단다. 그래 다음에 보자!












    

설정

트랙백

댓글

오랜 친구...

Photography/Memorials 2015. 2. 8. 01:30

2005년 어느 날부터 함께 살아온 녀석... 이제 집안의 소품처럼 제법 어울린다. 평소에 인식하지 못하는 공간의 자연스러움 때문에 어느 날, 그 고통은 더욱 크겠지만, 그때까지 건강하고 행복하기를....




    

설정

트랙백

댓글

올래장터에서 도네이션 파티를 진행합니다~

Miscellaneous/Etc 2015. 1. 15. 11:04

Donation Party!

2015.1월 31일 토요일 PM 7:00 !!


제가 소속된 올래장터 봉사모임에서 파티를 즐기면서 기부에 참여할 수 있는 기회를 제공하고자 여러분들을 초대합니다.

많은 분들이 기분 좋은 파티에 동참하셨으면 합니다. 친구들 손에 손 잡고 함께 즐겨주세요~



(상세 설명을 눌러주셔야 내용과 글을 보실수 있습니다 )

http://itempage3.auction.co.kr/DetailView.aspx?ItemNo=B221392739&keyword=%bf%c3%b7%a1&scoredtype=0


Allrae & Jumbo Elephant Collaboration!


올래가 마련한 2015년 첫 도네이션 파티가 열립니다!!


핫플레이스 청담동에 위치한 collage+에서 진행되는 이번 파티는 기부와 나눔에 관심있는 분들 외에도 다양한 분야의 내외국인, 아티스트, 트렌드세터와 셀럽들이 참여하는 Social Networking Party로 풍성한 볼거리와 퍼포먼스가 준비되어 있으니! 이번 파티 놓치면 후회하시겠죠? ^^


※ 도네이션 파티에서 센스 뽐내는 Tip

기부물품 가져오기!

집에 쓰지 않는 중고물품이나 나눔할 수 있는 물품이 있다면 파티당일 가져오세요~ 파티날에 한하여 기부물품을 가져오시는 분들에게 저희가 마련한 선물을 드립니다! 모여진 물품들은 후에 올래장터에서 판매합니다~


마음이 따뜻해 지는 시간, 모두 자리하셔서 함께 즐겨요~

* 파티로 얻어지는 수익은 전액 환우지원, 연탄나눔, 무료급식소 등 도움을 필요로하는 곳에 쓰입니다.



★ Performance 


- Masic Show

잠시도 눈을 뗄 수 없는 시간!


- 전근화

달달한 보이스로 여심을 녹여줄 감성 보컬♪


-로맨틱 멜로디 초비

로맨틱한 음악으로 마음을 간질여줄 싱어송라이터♪


- 고아라

前 댄스스포츠 선수의 화려한 댄스♬


- Round-2

열정적인 무대로 모두를 하나로 만들어줄 팝밴드♬



★ 경매 및 경품

퀄리티 높은 경매품과 어마어마한 경품이 여러분을 기다리고 있습니다!



★ 게스트 또는 VIP 문의

점보 010 5545 6915으로 문자 또는 카톡으로 연락 바랍니다. 



★ 장소: 꼴라쥬 +

서울시 강남구 청담동 91-5 B1 Collage+



★ one free drink ticket ₩20,000


Lucky Draw~ 


못오시는 분들이나 기부 또는 참여만 원하시는 분들은 올래기부통장 입금(국민은행 806202 04 164128) 및 모바일 옥션으로 티켓 구입 가능합니다. 


    

설정

트랙백

댓글

[Git]SourceTree에서 특정 파일 커밋에서 제외시키기

Programming/Git&SourceTree 2014. 11. 16. 15:51

이번 프로젝트를 진행하면서 SourceTree를 처음 접해보게 되었다. 제대로 활용하면 적은 노력으로 많은 도움을 받을 수 있을 것 같아서 하나하나 기록할 예정이다. SourceTree를 이용해서 Git을 사용하다 보면 커밋하지 않아도 되는 불필요한 파일이 있다. 대표적인 파일이 .DS_Store 이다. 


SourceTree에서는 이러한 파일을 커밋에서 무시할 수 있도록 설정을 변경할 수 있다. 간단하게 .gitignore 파일 수정을 통해서 .DS_Store 파일을 커밋에서 제외하는 방법은 아래와 같다.


오른쪽 상단에 있는 설정 아이콘을 클릭하여 설정 창을 연다.




Advanced 탭을 선택하면 상단에 Repository-specific ignore list 파일을 확인할 수 있다. Edit를 클릭한다.






편집 화면에서 하단에 */.DS_Store 을 추가한다.

기준 폴더의 위치에 따라서 하위 폴더에 생성되는 .DS_Store 파일이라면 ../.DS_Store 형식으로 디렉토리 구조로 대응하면 된다.



저장하면 앞으로 해당 폴더에서 .DS_Store 파일이 수정이 되더라도 커밋 대상에서 제외되기 때문에 앞으로는 Working Copy 항목에 포함되지 않는다.






    

설정

트랙백

댓글

요세미티와 iOS8을 이용해 디바이스 동영상 캡처하기

Programming/Objective-C 2014. 9. 25. 10:55

iOS8 업데이트와 함께 앱스토어에서 앱의 프리뷰 영상을 업로드 할 수 있도록 되었습니다. 기존에는 이러한 앱 영상을 만들려면 시뮬레이터를 이용한 녹화 방법 이외에 딱히 효과적인 방법을 찾기 어려웠습니다. 더욱이 디바이스 전용 하드웨어를 사용하는 앱의 경우에는 난감한 부분이었습니다. 그러나 요세미티의 QuickTime Player에서 디바이스 화면을 녹화할 수 있는 기능을 지원합니다. 


https://developer.apple.com/app-store/app-previews/


아래 링크가 좀 더 이해하기 쉽겠네요.

http://www.macworld.co.uk/how-to/iphone/create-video-for-your-app-on-app-store-3532435/


설명 드리면, 요세미티 베타 버전을 업데이트 하셨다면, 

1. 디바이스를 USB로 맥과 연결하고 QuickTime Player를 실행합니다.

2. 파일->새로운 동영상 녹화 선택

3. 동영상 화면에서 위 사진과 같이 녹화 버튼 옆에 화살표를 클릭.

4. 연결된 디바이스 중에 하나를 선택

5. 녹화

6. 끝.




    

설정

트랙백

댓글

iOS bitly.com을 통한 url 단축 코드

Programming/Objective-C 2013. 5. 29. 08:12

url 링크 스트링을 단축해주는 서비스들 중에 트위터에서 많이 사용되는 Bitly를 통한 url 단축 방법은 다음과 같다.


1. https://bitly.com/ 사이트에서 회원 가입을 한다. (페이스북이나 트위터가 로그인 되어 있으면 해당 계정으로 가입할 수 있음).

2. 오른쪽 상단 내 계정을 클릭, Settings 화면으로 이동하여 ADVANCED 탭을 선택.

3. 맨 아래 Legacy API Key에서 Show legacy API Key 를 클릭.

4. Login과 API Key를 복사해 둔다.


Objective-C 코드 :


-(NSString *)shortenedURL:(NSString *)url{
      NSString *bitLyLogin = @"yourLogin";
      NSString *bitLyAPIKey = @"yourAPIKey";
      NSString *shortenedURL = [NSString stringWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://api.bit.ly/v3/shorten?login=%@&apikey=%@&longUrl=%@&format=txt", bitLyLogin, bitLyAPIKey, url]] encoding:NSUTF8StringEncoding error:nil];
      
      return shortenedURL;
}


복사한 Login 스트링과 API Key를 넣으면 사용가능하다.


주의할 것은 Legacy API Key에서 API Key를 Reset하면 기존 API Key로는 사용할 수 없으므로 초기 설정된 값을 사용하거나

Reset한 API Key를 받아서 사용하도록 하자.


Facebook이나 Twitter로 가입을 한 경우, 초기 비밀번호를 설정해야 Bitly를 사용하는데 불편함이 없다. 위 코드로 링크를 단축할 경우 Bitly 사이트에서 단축한 원래의 url과 단축된 url을 확인할 수 있다. 또한, 해당 url로 얼마나 클릭이 이루어졌는지도 그래프로 확인이 가능하다.

    

설정

트랙백

댓글

iPhone, iPad : iOS font list

Programming/Objective-C 2012. 10. 5. 17:26


iPhone 4.2

iPad 4.2
Font Families: 40Font Families: 57
Academy Engraved LET
 AcademyEngravedLetPlain
American Typewriter
AmericanTypewriterAmericanTypewriter
AmericanTypewriter-BoldAmericanTypewriter-Bold
Apple Color Emoji
AppleColorEmojiAppleColorEmoji
AppleGothic
AppleGothicAppleGothic
Arial
ArialMTArialMT
Arial-ItalicMTArial-ItalicMT
Arial-BoldMTArial-BoldMT
Arial-BoldItalicMTArial-BoldItalicMT
Arial Hebrew
ArialHebrewArialHebrew
ArialHebrew-BoldArialHebrew-Bold
Arial Rounded MT Bold
ArialRoundedMTBoldArialRoundedMTBold
Bangla Sangam MN
BanglaSangamMNBanglaSangamMN
BanglaSangamMN-BoldBanglaSangamMN-Bold
Baskerville
BaskervilleBaskerville
Baskerville-ItalicBaskerville-Italic
Baskerville-BoldBaskerville-Bold
Baskerville-BoldItalicBaskerville-BoldItalic
Bodoni 72
 BodoniSvtyTwoITCTT-Book
 BodoniSvtyTwoITCTT-BookIta
 BodoniSvtyTwoITCTT-Bold
Bodoni 72 Oldstyle
 BodoniSvtyTwoOSITCTT-Book
 BodoniSvtyTwoOSITCTT-BookIt
 BodoniSvtyTwoOSITCTT-Bold
Bodoni 72 Smallcaps
 BodoniSvtyTwoSCITCTT-Book
Bodoni Ornaments
 BodoniOrnamentsITCTT
Bradley Hand
 BradleyHandITCTT-Bold
Chalkboard SE
ChalkboardSE-RegularChalkboardSE-Regular
ChalkboardSE-BoldChalkboardSE-Bold
Chalkduster
 Chalkduster
Cochin
CochinCochin
Cochin-ItalicCochin-Italic
Cochin-BoldCochin-Bold
Cochin-BoldItalicCochin-BoldItalic
Copperplate
 Copperplate
 Copperplate-Bold
Courier
CourierCourier
Courier-ObliqueCourier-Oblique
Courier-BoldCourier-Bold
Courier-BoldObliqueCourier-BoldOblique
Courier New
CourierNewPSMTCourierNewPSMT
CourierNewPS-ItalicMTCourierNewPS-ItalicMT
CourierNewPS-BoldMTCourierNewPS-BoldMT
CourierNewPS-BoldItalicMTCourierNewPS-BoldItalicMT
DB LCD Temp
DBLCDTempBlackDBLCDTempBlack
Devanagari Sangam MN
DevanagariSangamMNDevanagariSangamMN
DevanagariSangamMN-BoldDevanagariSangamMN-Bold
Didot
 Didot
 Didot-Italic
 Didot-Bold
Futura
Futura-MediumFutura-Medium
Futura-MediumItalicFutura-MediumItalic
Futura-CondensedExtraBoldFutura-CondensedExtraBold
Geeza Pro
GeezaProGeezaPro
GeezaPro-BoldGeezaPro-Bold
Georgia
GeorgiaGeorgia
Georgia-ItalicGeorgia-Italic
Georgia-BoldGeorgia-Bold
Georgia-BoldItalicGeorgia-BoldItalic
Gill Sans
 GillSans
 GillSans-Italic
 GillSans-Bold
 GillSans-BoldItalic
Gujarati Sangam MN
GujaratiSangamMNGujaratiSangamMN
GujaratiSangamMN-BoldGujaratiSangamMN-Bold
Gurmukhi MN
GurmukhiMNGurmukhiMN
GurmukhiMN-BoldGurmukhiMN-Bold
Heiti J
STHeitiJ-LightSTHeitiJ-Light
STHeitiJ-MediumSTHeitiJ-Medium
Heiti K
STHeitiK-LightSTHeitiK-Light
STHeitiK-MediumSTHeitiK-Medium
Heiti SC
STHeitiSC-LightSTHeitiSC-Light
STHeitiSC-MediumSTHeitiSC-Medium
Heiti TC
STHeitiTC-LightSTHeitiTC-Light
STHeitiTC-MediumSTHeitiTC-Medium
Helvetica
HelveticaHelvetica
Helvetica-ObliqueHelvetica-Oblique
Helvetica-BoldHelvetica-Bold
Helvetica-BoldObliqueHelvetica-BoldOblique
Helvetica Neue
HelveticaNeueHelveticaNeue
HelveticaNeue-ItalicHelveticaNeue-Italic
HelveticaNeue-BoldHelveticaNeue-Bold
HelveticaNeue-BoldItalicHelveticaNeue-BoldItalic
Hiragino Kaku Gothic ProN
HiraKakuProN-W3HiraKakuProN-W3
HiraKakuProN-W6HiraKakuProN-W6
Hiragino Min ProN
 HiraMinProN-W3
 HiraMinProN-W6
Hoefler Text
 HoeflerText-Regular
 HoeflerText-Italic
 HoeflerText-Black
 HoeflerText-BlackItalic
Kailasa
KailasaKailasa
Kailasa-BoldKailasa-Bold
Kannada Sangam MN
KannadaSangamMNKannadaSangamMN
KannadaSangamMN-BoldKannadaSangamMN-Bold
Malayalam Sangam MN
MalayalamSangamMNMalayalamSangamMN
MalayalamSangamMN-BoldMalayalamSangamMN-Bold
Marker Felt
MarkerFelt-ThinMarkerFelt-Thin
MarkerFelt-WideMarkerFelt-Wide
Optima
 Optima-Regular
 Optima-Italic
 Optima-Bold
 Optima-BoldItalic
Oriya Sangam MN
OriyaSangamMNOriyaSangamMN
OriyaSangamMN-BoldOriyaSangamMN-Bold
Palatino
Palatino-RomanPalatino-Roman
Palatino-ItalicPalatino-Italic
Palatino-BoldPalatino-Bold
Palatino-BoldItalicPalatino-BoldItalic
Papyrus
 Papyrus
Party LET
 PartyLetPlain
Sinhala Sangam MN
SinhalaSangamMNSinhalaSangamMN
SinhalaSangamMN-BoldSinhalaSangamMN-Bold
Snell Roundhand
SnellRoundhandSnellRoundhand
SnellRoundhand-BoldSnellRoundhand-Bold
Tamil Sangam MN
TamilSangamMNTamilSangamMN
TamilSangamMN-BoldTamilSangamMN-Bold
Telugu Sangam MN
TeluguSangamMNTeluguSangamMN
TeluguSangamMN-BoldTeluguSangamMN-Bold
Thonburi
 Thonburi
 Thonburi-Bold
Times New Roman
TimesNewRomanPSMTTimesNewRomanPSMT
TimesNewRomanPS-ItalicMTTimesNewRomanPS-ItalicMT
TimesNewRomanPS-BoldMTTimesNewRomanPS-BoldMT
TimesNewRomanPS-BoldItalicMTTimesNewRomanPS-BoldItalicMT
Trebuchet MS
TrebuchetMSTrebuchetMS
TrebuchetMS-ItalicTrebuchetMS-Italic
TrebuchetMS-BoldTrebuchetMS-Bold
Trebuchet-BoldItalicTrebuchet-BoldItalic
Verdana
VerdanaVerdana
Verdana-ItalicVerdana-Italic
Verdana-BoldVerdana-Bold
Verdana-BoldItalicVerdana-BoldItalic
Zapf Dingbats
 ZapfDingbatsITC
Zapfino
ZapfinoZapfino

출처 : http://www.michaelcritz.com/2010/04/02/fonts-for-ipad-iphone/

    

설정

트랙백

댓글

iOS icon 파일명 및 크기

Programming/Objective-C 2012. 9. 5. 00:39

이미지(px)

파일명

사용처

요구상태기타
512x512iTunesArtworkAd Hoc iTunes

선택적 필요

파일은 png 형식이어야 함, 그러나 이름에서는 .png 확장 제외
57x57Icon.png아이폰/아이팟 터치 앱스토어와 홈 화면필요<없음>
114x114Icon@2x.pngiPhone 4 고해상 홈 화면선택적 필요

< 없음>

72x72Icon-72.png

iPad 호환 홈 화면

선택적 필요

< 없음>

29x29Icon-Small.png

Spotlight와 설정

선택적 필요<없음>
50x50Icon-Small-50.pngiPad 호환 Spotlight설정된 bundle이 있다면 필요, 아니면 선택적 필요<없음>
58x58Icon-Small@2x.png

Spotlight와 iPhone 4 고해상도

설정된 bundle이 있다면 필요, 아니면 선택적 필요<없음>

스샷: 320 x 480 (1~4장)


Splash:

Default.png : 320 x 480

Default@2x.png : 640 x 960

+

iPhone5 해상도 지원 이미지 : 640 x 1136


    

설정

트랙백

댓글

Release build에서 NSLog 무효화

Programming/Objective-C 2012. 8. 31. 15:14

prefix header에 아래 코드 삽입.

Release 빌드할 때 무효화 자동 처리.


#ifndef __OPTIMIZE__

#define NSLog(...) NSLog(__VA_ARGS__)

#else

#define NSLog(...) {}

#endif

    

설정

트랙백

댓글

NSDate to String

Programming/Objective-C 2012. 7. 23. 19:45

PatternResult (in a particular locale)
yyyy.MM.dd G 'at' HH:mm:ss zzz1996.07.10 AD at 15:08:56 PDT
EEE, MMM d, ''yyWed, July 10, '96
h:mm a12:08 PM
hh 'o''clock' a, zzzz12 o'clock PM, Pacific Daylight Time
K:mm a, z0:00 PM, PST
yyyyy.MMMM.dd GGG hh:mm aaa01996.July.10 AD 12:08 PM




-(void) date1{
      // output : 7/23/12
      NSDate *today = [NSDate dateWithTimeIntervalSinceNow:0];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateStyle:NSDateFormatterShortStyle];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date1 : %@", dateString);
}

-(void) date2{
      // output : 07/23/2012 07:36PM
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"MM/dd/yyyy hh:mma"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date2 : %@", dateString);
      
}
-(void) date3{
      // output : Monday July 23, 2012
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"EEEE MMMM d, YYYY"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date3 : %@", dateString);
}

-(void) date4{
      // output : 7:38 PM, GMT+09:00
      NSDate *today = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"h:mm a, zzz"];
      NSString *dateString = [dateFormat stringFromDate:today];
      NSLog(@"date4 : %@", dateString);
}

-(void) date5{
      // output : 월요일 7월 23, 2012
      NSDate *date = [NSDate date];
      NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
      [dateFormat setDateFormat:@"EEEE MMMM d, YYYY"];
      [dateFormat setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"ko_kr"]];
      NSString *dateString = [dateFormat stringFromDate:date];
      NSLog(@"date5 : %@", dateString);
}


    

설정

트랙백

댓글

becomeFirstResponder, resignFirstResponder 애니메이션 duration time 설정

Programming/Objective-C 2012. 7. 22. 04:48

UITextField 를 터치하면 나타나거나 사라지는 keypad의 애니메이션 속도를 조정하기 위해서는 아래와 같은 방법을 사용할 수 있다.



[UIView animateWithDuration:0.3 animations:^{
                  [inputText becomeFirstResponder];
            }];

[UIView animateWithDuration:0.3 animations:^{
                  [inputText resignFirstResponder];
            }];


애니메이션 종료 시점에 처리해야하는 부분이 있으면 간단하게 아래와 같이 처리할 수도 있다. (delegate 메소드를 사용하는 것을 권장.)


[UIView animateWithDuration:0.3 animations:^{
                  [inputText becomeFirstResponder];      
            } completion:^(BOOL finished) {
                  // 애니메이션 종료후 처리
            }];



애니메이션 없이 바로 나타나게 하려면 UIView animate duration time을 0으로 설정하면 된다. 



    

설정

트랙백

댓글

iOS의 SQLite-FMDB 사용

Programming/Objective-C 2012. 4. 27. 10:29

iOS에서 데이터를 저장하는 방식 중에는 크게 번들 plist 파일에 저장하는 방법과 SQLite로 db방식으로 저장하는 방법이 있다. plist는 파일 형식으로 저장하는 방법, SQLite는 db 형식으로 저장하는 방법으로 이해할 수 있는데 SQLite를 사용하기 위해서는 db를 쓰고 읽는 기능을 하기 위해서는 적지 않은 코드를 작성해야하는 번거로움이 있다. 이를 해결하고자 FMDB라는 Wrapper library를 GitHub에서 배포하고 있다. 정리 차원에서 일본 블로거 포스트 내용을 번역하여 정리해 본다.


1. FMDB 란?

FMDB는 SQLite를 iOS의 Objective-C에서 사용하기 편하도록 Wrapper Library를 GitHub에서 공개하고 있다. 

https://github.com/ccgus/fmdb


인터페이스는 JDBC 또는 ADO.NET에 가깝다. 따라서, 이것을 사용한 적이 있으면 원활하게 이해할 수 있을 것이다.



2. FMDB 준비

먼저 FMDB를 이용하고 싶은 프로젝트에 SQLite 라이브러리를 추가한다. 

1. Xcode 왼쪽 창의 탐색창에서 프로젝트를 선택 

2. 오른쪽 창에 PROJECT와 TARGET이 표시되며 TARGET을 선택

3. 오른쪽 창, 상단 탭에서 Build Phases 선택

4. 빌드에 대한 설정 항목이 표시되는데 그 중에서 Link Binary With Libraries를 선택

5. Link Binary With Libraries 왼쪽 아래에 +를 클릭

6. Choose framework and libraries add :라는 메시지가 표시되고

7. 검색을 통해서 프레임워크와 라이브러리 목록에서 libsqlite3.0.dylib 선택 

8. 대화 상자 오른쪽 아래 Add 버튼을 클릭


그 다음으로 FMDB 소스를 프로젝트에 추가한다.

1. FMDB 프로젝트 페이지에서 ZIP 파일을 내려 받는다.

2. 압축을 풀고 src 폴더에서 fmdb.m 파일을 제외하고 프로젝트에 import

3. 



3. 데이터베이스 만들기 및 open / close

데이터베이스를 제공하는 경우 미리 만든 데이터베이스 파일을 프로젝트 리소스에 통합하고 그 것을 복사하거나 SQLite를 생성해야한다. iOS 애플리케이션 작업 영역에 app.db라는 데이터베이스 파일을 생성하는 경우는 다음과 같이 처리한다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *dir = [paths objectAtIndex : 0]; FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]]; NSLog(@"%@", db); [db open]; [db close];

databaseWithPath 메소드에 파일의 경로를 지정하여 파일이 기존에 존재하면 참조하고 없을 경우에는 새로 만들고 연결된 FMDatabase 인스턴스를 반환한다.


데이터베이스 작업을 시작하면 이 인스턴스에 대한 open 메소드를 호출한다. 종료는 close 메소드, close를 호출하면 데이터베이스를 닫고 변경된 내용을 파일에 저장한다. 


4. CREATE

테이블 생성은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];
NSString *sql = @"CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);";
[db open];
[db executeUpdate:sql];
[db close];


FMDatabase - executeUpdate 메소드에 CREATE 문장을 지정하여 작업을 수행한다. SQLite는 IF NOT EXISTS에 대응하고, 이 것을 붙이면 테이블이 존재하지 않을 때만 생성 해주기 때문에 편리하다.


5. INSERT

행을 추가하는 것은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];
NSString *sql = @"INSERT INTO users (name) VALUES (?)";

[db open];
[db executeUpdate:sql, @"이름" ];
[db close];


executeUpdate는 Prepared Statement에 대응하고 있다. SQL 문장에서 ?를 작성한 매개변수 부분에 가변 인자를 할당한다.



6. DELETE

행 삭제는 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"DELETE FROM users WHERE id =?";
[db open];
[db executeUpdate:sql, [NSNumber numberWithInteger:14]];
[db close];


7. SELECT

데이터 선택은 다음과 같다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"SELECT id, name FROM users;";
[db open];

FMResultSet *results = [db executeQuery:sql];
NSMutableArray *users = [[NSMutableArray alloc] initWithCapacity:0];

while ([results next])
{
      User *user = [[User alloc] init];
      user.userId = [results intForColumnIndex:0];
      user.name = [results stringForColumnIndex:2];
      [users addObject:user];
}

[db close];


FMDatabase - executeQuery 처리 결과를 FMResultSet 인스턴스로 반환한다.

FMResultSet - next를 호출하여 가져온 행을 순차적으로 불러온다. 행이 있는 경우 YES, 끝이면 NO를 반환하므로 이 메소드를 호출하는 while 조건문을 통해서 모든 행이 열거된다.



8. 트랜잭션

SQLite는 암묵적으로 트랜잭션 제어를 한다. 평상시에는 문제가 없지만 대량의 INSERT를 실행하면 그때마다 BEGIN ~ COMMIT이 실행되기 때문에 속도가 크게 저하된다. 이러한 작업을 수행하려면 대상 구간을 명시적으로 트랜잭션 처리하는 것이 좋다.


FMDB가 명시적으로 트랜잭션 FMDatabase - beginTransaction 메서드에서 시작되고 commit으로 종료된다. 오류 등으로 처리 전의 상태로 되돌리려면 rollback 메소드를 실행한다.


NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex : 0];
FMDatabase *db = [FMDatabase databaseWithPath:[dir stringByAppendingPathComponent:@"app.db"]];

NSString *sql = @"SELECT id, name FROM users;";
[db open];

FMResultSet *results = [db executeQuery:sql];
NSMutableArray *users = [NSMutableArray array];

while ([results next])
{
      User *user = [[User alloc] init];
      user.userId = [results intForColumnIndex:0];
      user.name = [results stringForColumnIndex:2];
      NSLog(@"%@", user.name);
      [users addObject:user];
}

[db close];


// Transaction
sql = @"INSERT INTO users (name) VALUES (?)";

[db open];
[db beginTransaction];

BOOL isSucceeded = YES;
for( User* user in users )
{
      NSLog(@"name:%@", user.name);
      if( ![db executeUpdate:sql, user.name] )
      {
isSucceeded = NO;
break;
      }
}

if( isSucceeded )
{
      [db commit];
}
else
{
      [db rollback];
}

[db close];

명시적 트랜잭션을 실행하는 동안에는 데이터베이스 전체가 잠긴다. 따라서 구간 내에서 새로운 데이터베이스를 open하면 응답 없음으로 간주한다.



9. 형식

FMDB를 이용하여 데이터를 취득, 설정하는 형식에 대한 정리.


데이터를 취득하기 위해서는 FMDatabase - executeQuery의 결과로 반환된 FMResultSet 인스턴스 메소드를 사용한다. 메소드 이름은 typeForColumn 또는 typeForColumnIndex 다. 각 컬럼 이름과 위치 인덱스 값을 얻을 수 있다.


예를 들어, stringForColumn에 "name"을 지정했다면 name 문자열(TEXT)의 컬럼을 가져온다. intForColumnIndex:4를 지정하면 SELECT 문을 검색할 4 번째 정수(INTEGER)를 얻을 수 있다. 대표적인 데이터 형식 대응은 아래와 같다.


SQLite

Objective-C 

검색 메소드 

 TEXT

NSString 

stringForColumn, stringForColumnIndex 

INTEGER 

int 

intForColumn, intForColumnIndex 

BOOL 

BOOL 

boolForColumn, boolForColumnIndex 

REAL 

double 

doubleForColumn, doubleForColumnIndex 

DATETIME (INTEGER, REAL, TEXT)

NSDate 

dateForColumn, dateForColumnIndex 

BLOB 

NSData 

dataForColumn, dataForColumnIndex 


FMDatabase - executeQuery로 지정하는 매개변수는 런타임에 유형검사를 한다. TEXT면 NSString, INTEGER면 NSNumber로 대응하며 잘 못된 형식을 지정하면 런타임 에러를 발생시킨다.


데이터 취득의 경우,  FMResultSet이 Objective-C의 변환을 담당하지만 설정시에 변환은 직접 해야한다. 변환 대응은 아래와 같다.


 SQLite

Objective-C 

변환 방법 

 TEXT

NSString 

변환 불필요 

INTEGER 

NSInteger 

NSNumber - numberWithInt 

BOOL 

BOOL 

NSNumber - numberWithBool 

REAL 

double 

NSNumber - numberWithDouble 

DATETIME (INTEGER, REAL, TEXT) 

NSDate 

변환 불필요 

BLOB 

NSData 

변환 불필요. 


SQLite의 날짜 형식은 다음과 같이 정의되어 있다. (Datatypes In SQLite Version 3 에서 인용).


TEXT as ISO8601 strings (“YYYY-MM-DD HH:MM:SS.SSS”).

REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.

INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.


DATETIME은 고유의 형식이 있는 것은 아니고 TEXT, REAL, INTEGER를 사용한다. SQLite 클라이언트는 TEXT 밖에 허용하지 않으며 System.Data.SQLite과 같이 연결 문자열 DATETIME 형식의 해석 방법을 선택할 수 있다. 

FMDB의 경우 내부적으로 Unix Timestamp에서 운용하고 있으므로 형식을 정의할 때 INTEGER로 하면 좋다. 



10. Lita(데이터베이스 관리 프로그램)

FMDB를 이용한 데이터베이스 작업을 했을 때 그 결과가 어떻게 반영되었는지 확인하고 싶을 때가 있다. 또한 애플리케이션에서 사용하는 SQL 문을 검토하기 위해 실제 데이터베이스에 대해 SQL을 실행하고 싶은 경우도 많다. 이럴 때 사용하면 유용한 AIR 애플리케이션이 바로 Lita 다.


Lita – SQLite Administration Tool


iPhone 시뮬레이터를 통해 SQLite 데이터베이스를 생성한 경우, Mac의 다음 위치에 파일이 생성된다.

/Users/사용자이름/Library/Application Support/iPhone Simulator/5.0/Applications/애플리케이션 ID/Documents


만들어진 파일을 Lita에서 열고 애플리케이션이 데이터베이스 작업을 행한 후, Lita에서 갱신하면 저장된 데이터의 변경을 쉽게 할 수 있다. 





    

설정

트랙백

댓글

iOS XML parser NSXMLParser[2]

Programming/Objective-C 2012. 4. 21. 19:11

이전 포스트에서는 NSXMLParser를 사용할 때 호출되는 기본적인 델리게이트 메소드들을 살펴보았다. 이번에는 각 element및 attribute 값들을 객체를 이용하여 저장해 본다. 저장 방식은 iOS5 programming cookbook 샘플소스를 수정해서 사용했다.


먼저 사용할 xml 데이터는 아래와 같은 형식이다.

<?xml version="1.0" encoding="UTF-8"?>
<root>
      <person id="1">
            <firstName>Anthony</firstName>
            <lastName>Robbins</lastName>
            <age>51</age>
      </person>

      <person id="2">
            <firstName>Richard</firstName>
            <lastName>Branson</lastName>
            <age>61</age>
      </person>
</root>


아래는 개별 element들을 저장하기 위한 클래스다. XML 트리형식의 구조로 상위 element 데이터에 접근할 수 있도록 parent라는 자신과 같은 객체 형식의 참조 속성을 포함하고 있다. 단순히 참조하는 형태이기 때문에 weak로 생성한다.


XMLElement.h

#import <Foundation/Foundation.h>

@interface XMLElement : NSObject

@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *text;
@property (nonatomic, strong) NSDictionary *attributes;
@property (nonatomic, strong) NSMutableArray *subElements;
@property (nonatomic, weak) XMLElement *parent;

@end


XMLElement.m

#import "XMLElement.h"

@implementation XMLElement

@synthesize name;
@synthesize text;
@synthesize attributes;
@synthesize subElements;
@synthesize parent;

- (NSMutableArray *) subElements{
  if (subElements == nil){
    subElements = [[NSMutableArray alloc] init];
  }
  return subElements;
}

@end


subElements 최초 참조시 객체가 생성되지 않았을 때는 생성해서 return하도록 처리되어 있다. 

아래는 메인 델리게이트 클래스다.


#import <UIKit/UIKit.h>

@class XMLElement;

@interface Parsing_XML_with_NSXMLParserAppDelegate
           : UIResponder <UIApplicationDelegate, NSXMLParserDelegate>

@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) NSXMLParser *xmlParser;
@property (nonatomic, strong) XMLElement *rootElement;
@property (nonatomic, strong) XMLElement *currentElementPointer;

@end



#import "Parsing_XML_with_NSXMLParserAppDelegate.h"
#import "XMLElement.h"

@implementation Parsing_XML_with_NSXMLParserAppDelegate

@synthesize window = _window;
@synthesize xmlParser;
@synthesize rootElement;
@synthesize currentElementPointer;

- (void)parserDidStartDocument:(NSXMLParser *)parser{
      self.rootElement = nil;
      self.currentElementPointer = nil;
}

- (void)parserDidEndDocument:(NSXMLParser *)parser{
      self.currentElementPointer = nil;
}

- (void)        parser:(NSXMLParser *)parser 
       didStartElement:(NSString *)elementName 
          namespaceURI:(NSString *)namespaceURI
         qualifiedName:(NSString *)qName
            attributes:(NSDictionary *)attributeDict{
  
      if (self.rootElement == nil){
            /* 루트 element가 생성되지 않았을 때 */
            
            self.rootElement = [[XMLElement alloc] init];
            self.currentElementPointer = self.rootElement;
      } else {
            /* 루트 element가 있을 때 새로운 XMLElement 객체를 만들고 현재 참조 element를 parent로
             새로 만들어진 element를 현재 참조 element로 연결 */
            
            XMLElement *newElement = [[XMLElement alloc] init];
            newElement.parent = self.currentElementPointer;
            [self.currentElementPointer.subElements addObject:newElement];
            self.currentElementPointer = newElement;
      }

      self.currentElementPointer.name = elementName;
      self.currentElementPointer.attributes = attributeDict;
  
}

- (void)        parser:(NSXMLParser *)parser
         didEndElement:(NSString *)elementName
          namespaceURI:(NSString *)namespaceURI
         qualifiedName:(NSString *)qName{
      
      /* element를 빠져 나올 때 현재 XMLElement에서 parent로 참조하고 있는 element를
       현재 참조로 대입 */
      self.currentElementPointer = self.currentElementPointer.parent;
  
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
  
      self.currentElementPointer.text = string;

}

- (BOOL)            application:(UIApplication *)application 
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

      NSString *xmlFilePath = [[NSBundle mainBundle] pathForResource:@"MyXML"
                                                          ofType:@"xml"];

      NSData *xml = [[NSData alloc] initWithContentsOfFile:xmlFilePath];

      self.xmlParser = [[NSXMLParser alloc] initWithData:xml];
      
      self.xmlParser.delegate = self;

      if ([self.xmlParser parse]){
            NSLog(@"The XML is parsed.");
            XMLElement *element;
            XMLElement *subElement;
            for(element in self.rootElement.subElements){
                  NSLog(@"person ID : %@", [element.attributes objectForKey:@"id"]);
                  for(subElement in element.subElements){
                        NSLog(@"\t%@ : %@", subElement.name, subElement.text);
                  }
            }
      } else{
            NSLog(@"Failed to parse the XML");
      }
  
      self.window = [[UIWindow alloc] initWithFrame:
                 [[UIScreen mainScreen] bounds]];

      self.window.backgroundColor = [UIColor whiteColor];
      [self.window makeKeyAndVisible];
      return YES;
}
@end


위 코드에서 가장 핵심이 되는 부분은 아래 부분이다. 새로은 element를 생성한 후, 생성된 element객체 parent를 현재 참조하고 있는 element로 설정한다. 이는 현재 element 에서 자식 element가 새롭게 생성되어 연결되는 과정을 보여준다. 새로은 element를 addObject로 추가하고 마지막으로는 새롭게 생성된 element를 현재 element에 대입하여 새롭게 생성된 element가 현재 element가 되도록 처리해 준다. 

XMLElement *newElement = [[XMLElement alloc] init]; newElement.parent = self.currentElementPointer; [self.currentElementPointer.subElements addObject:newElement]; self.currentElementPointer = newElement;


다음은 출력된 결과다.


The XML is parsed.
person ID : 1
	firstName : Anthony
	lastName : Robbins
	age : 51
person ID : 2
	firstName : Richard
	lastName : Branson
	age : 61












    

설정

트랙백

댓글

iOS XML parser NSXMLParser[1]

Programming/Objective-C 2012. 4. 20. 00:43

iOS에서 사용할 수 있는 XML 파서가 꽤 많다. 대체적으로 메모리 사용량이나 속도면에서 TBXML을 많이 사용하는 듯 하다. 무료도 제공되는 라이브러리가 성능면에서는 좋은 것 같은데... 문제는 iOS의 빠른 업데이트를 따라가려면 좀 시간이 걸린다는 것... TBXML에서는 아직 xCode에서 업데이트 된 ARC(Automatic Reference Counting)를 지원하지 않는 것 같아서 일단 기본 제공되는 NSXMLParser 사용법 부터 정리해 보기로 한다.


NSXMLParser도 delegate 메소드를 통해서 element의 값들에 접근할 수 있다. iOS를 공부하면서 가장 이해하기 어려웠던 부분이 바로 delegate 메소드 호출방식이었다. 전혀 속도면에서 도움이 될 것 같지 않은 방법을 채택하고 있는지라 초기에는 왜 이렇게 해야만하는지에 대한 의문에 사로잡혀 있다보니 한 없이 복잡하고 어려웠다. 아무튼 공부는 이해가 안되면 그냥 정신줄을 놓고 외워서 받아들이는 편이 정신 건강에 좋다.


본론으로 들어가서... 본인도 틈틈히 개인적으로 공부하는 차원에서 정리하고 있기 때문에 내용에서 오류가 발견될 수도 있다. 오류가 있으면 알려주시면 좋겠다. 


NSXMLParser는 parse 메소드에 메시지를 전달하면 delegate로 연결된 객체에서 처리해 놓은 일련의 메소드들을 호출하면서 파싱을 진행한다. 아래는 NSXMLParser가 파싱을 하면서 실행되는 메소드들의 순서다.


NSXMLParser 객체 parse 메소드 호출 후,

1. parserDidStartDocument

2. didStartElement

3. foundCharacters

4. didEndElement

5. parserDidEndDocument


이 과정에서 오류가 발견되면 parseErrorOccurred 메소드가 호출된다.


아래는 샘플로 활용할 xml 내용이다.


<?xml version="1.0" encoding="UTF-8"?> <catalog> <book id="bk101"> <author>Gambardella, Matthew</author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> </catalog>


일단 번들에 있는 xml 데이터를 불러와서 파싱을 시작한다.

NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"sample.xml"];
    NSData *data = [NSData dataWithContentsOfFile:path];
    
    self.xmlParser = [[NSXMLParser alloc] initWithData:data];
    self.xmlParser.delegate = self;
    if([self.xmlParser parse]){
        NSLog(@"The XML is Parsed.");
        
    }else {
        NSLog(@"Failed to parse the XML");
    }

[self.xmlParser parse] 메소드를 호출하여 파싱을 시작하면 처음으로 호출되는 메소드는 parserDidStartDocument 

-(void) parserDidStartDocument:(NSXMLParser *)parser{
      NSLog(@"parserDidStartDocument");
      // tab 처리를 위한 전역 변수
      tabString = [NSMutableString new];
}

그리고 순차적으로 element들을 탐색하기 시작한다. 각 element에 접근할 때마다 아래 메소드가 실행된다.


-(void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName 
                                          namespaceURI:(NSString *)namespaceURI 
                                          qualifiedName:(NSString *)qName 
                                          attributes:(NSDictionary *)attributeDict{
      tabString = [[tabString stringByAppendingString:@"\t"] mutableCopy];
      NSLog(@"%@", [tabString stringByAppendingString:elementName]);
      
}

elementName가 각 element명이다. 여기서는 NSLog 쓸 때 tab을 하나씩 추가하여 들여쓰기를 한다.  해당 노드의 attribute들은 NSDictionary형으로 attributeDict을 참조해서 사용하면 된다.


그 다음으로 실행되는 것이 아래 메소드다.


-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
      NSLog(@"%@", [tabString stringByAppendingString:string]);
}

didStartElement 메소드에서 접근한 element의 value 값을 확인할 수 있다. 그 다음으로는 element의 값까지 접근했으니 element가 닫히는 부분이다. 여기서는 일단 elementName을 출력하고 tabString의 끝에 있는 '\t'을 하나씩 제거하여 들여쓰기를 전 단계로 되돌린다.


-(void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName 
                                          namespaceURI:(NSString *)namespaceURI 
                                          qualifiedName:(NSString *)qName{
      NSLog(@"%@", [tabString stringByAppendingString:elementName]);
      tabString = [[tabString substringToIndex:[tabString length]-1] mutableCopy];
}

 

이렇게 뎁스 안쪽으로 탐색을 하여 값을 참조하고 밖으로 나오면서 최종 root element를 닫으면 아래 메소드가 실행되고 parsing을 종료하게 된다. 


-(void) parserDidEndDocument:(NSXMLParser *)parser{
      NSLog(@"parserDidEndDocument");
      tabString = nil;
}


output 결과 >

parserDidStartDocument
	catalog
	
   
		book
		
      
			author
			Gambardella, Matthew
			author
		
      
			title
			XML Developer's Guide
			title
		
      
			genre
			Computer
			genre
		
      
			price
			44.95
			price
		
      
			publish_date
			2000-10-01
			publish_date
		
      
			description
			An in-depth look at creating applications 
      with XML.
			description
		
   
		book
	

	catalog
parserDidEndDocument
The XML is Parsed.


element value가 없는 것들도 메소드는 실행되기 때문에 줄 내림이 생기는 것을 알 수 있다. 

이제 해야할 일은 각 element와 attribute에 접근하여 값을 확인할 수 있으니 이것을 나름의 데이터에 저장해서 활용하면 된다. 다음 포스트에서는 참조된 데이터를 저장하여 사용하는 부분을 추가하도록 하겠다.



    

설정

트랙백

댓글

형식이 있는 문자열이 사용할 수 있는 토큰

Programming/Objective-C 2012. 4. 10. 13:20

Objective-C에서 형식이 있는 문자열이 사용할 수 있는 토큰

심벌표시
 %@ id
 %d, %D, %ilong
 %u, %Uunsigned long
 %hishort
 %huunsigned short
 %qilong long
 %quunsigned long
 %x, %X16진법으로 표시한 unsigned long
 %o, %O8진법으로 표시한 unsigned long
 %f, %e, %E, %g, %Gdouble
 %cASCII 문자인 unsigned char
 %C유니코드 문자인 unichar
 %schar* (ASCII 문자들. NULL로 끝을 나타내는 C 문자열)
 %Sunichar* (유니코드 문자들. NULL로 끝을 나타내는 C 문자열)
 %pvoid* (0x로 시작하는 16진법으로 표시한 주소)
 %%% 문자



    

설정

트랙백

댓글

UIColor 매크로(RGB, RGBA, HTML)

Programming/Objective-C 2012. 4. 4. 17:49


#define RGB(r, g, b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]

#define RGBA(r, g, b, a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]

#define HTML(rgb) RGB((double)(rgb >> 16 & 0xff), (double)(rgb >> 8 & 0xff), (double)(rgb & 0xff))

//////////////////////////////////////////////////////////////////////

// Code without the macro

  msgLabel.textColor = [UIColor colorWithRed:255/255.0 green:251/255.0 blue:204/255.0 alpha:1];

// Or like this...

  msgLabel.textColor = [UIColor colorWithRed:1.0 green:.98 blue:.8 alpha:1];

 

// Code with macro

      msgLabel.textColor = RGB(255, 251, 204);

[label setTextColor:HTML(0xff0000)];


참고 : http://iphonedevelopertips.com/cocoa/uicolor-macros.html









    

설정

트랙백

댓글

AIR 3 & FP11 - RTMFP + Starling framework

Project/Programming 2012. 1. 17. 11:36
    

설정

트랙백

댓글

Starling 이미지 로드 및 scale & rotation

Programming/Framework 2012. 1. 12. 10:52

package {
	import starling.events.Event;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.Button;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.animation.Tween;
	import starling.animation.Transitions;
	import starling.utils.deg2rad;
	import starling.display.DisplayObject;
	import starling.text.TextField;
    import starling.utils.HAlign;
    import starling.utils.VAlign;
    
    import utils.TouchSheet;
	
	import flash.geom.Rectangle;
	import flash.geom.Point;
	import flash.display.Bitmap;
	import flash.net.URLRequest;
	import flash.display.Loader;
	import flash.events.Event;

	public class Demo extends Sprite {

		private var _loader:Loader;
		private var _image:Image;
		private var _posText:TextField;
		private var _sheet:TouchSheet;
		
		public function Demo() {
			_loader = new Loader();
			_loader.load(new URLRequest("https://t1.daumcdn.net/cfile/tistory/11252E424F0ADA1C06"));
			_loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE, onLoadedHandler);
		}

		
		private function onLoadedHandler(e:flash.events.Event):void{
			var bitmap:Bitmap = _loader.content as Bitmap;
			var texture:Texture = Texture.fromBitmap(bitmap);
			_image = new Image(texture);
			
			
			 var description:String = 
                "- touch and drag to move the images \n" +
                "- pinch with 2 fingers to scale and rotate \n" +
                "- double tap brings an image to the front \n" +
                "- use Ctrl/Cmd & Shift to simulate multi-touch";
            
            var infoText:TextField = new TextField(300, 75, description);
            infoText.x = 10;
			infoText.y = 35;
			infoText.fontSize = 12;
			infoText.color = 0x999999;
            infoText.vAlign = VAlign.TOP;
            infoText.hAlign = HAlign.LEFT;
            addChild(infoText);
			
			_sheet = new TouchSheet(_image);
			_sheet.scaleX = 0.2;
			_sheet.scaleY = 0.2;
			setAlignCenter(_sheet);
			_sheet.addEventListener(TouchEvent.TOUCH, onTouchHandler);
			addChild(_sheet);
			
			_posText = new TextField(400, 480, "");
			_posText.x = 10;
			_posText.y = 105;
			_posText.fontSize = 12;
			_posText.color = 0xBBBBBB;
            _posText.vAlign = VAlign.TOP;
            _posText.hAlign = HAlign.LEFT;
			_posText.touchable = false;
            addChild(_posText);
			
			stage.addEventListener(starling.events.Event.RESIZE, onResizeHandler);
			
		}
		
		private function onTouchHandler(e:TouchEvent):void{
			var touches:Vector.<Touch> = e.getTouches(_sheet);
			_posText.text = "_sheet.x : "+_sheet.x+"\n"+
							"_sheet.y : "+_sheet.y+"\n"+
							"_sheet.width  : "+_sheet.width+"\n"+
							"_sheet.height : "+_sheet.height;
			
			var len:int = touches.length;
			for(var i:int=0;i<len;i++){
				var touch:Touch = touches[i];
				var currentPoint:Point = touch.getLocation(_sheet);
				var previousPoint:Point = touch.getPreviousLocation(_sheet);
				_posText.text +="\n\n"+"touches["+i+"]========================\n"+
								"previousGlobalX : "+touch.previousGlobalX+"\n"+
								"previousGlobalY : "+touch.previousGlobalY+"\n"+
								"globalX : "+touch.globalX+"\n"+
								"globalY : "+touch.globalY+"\n"+
								"getLocation().x : "+currentPoint.x+"\n"+
								"getLocation().y : "+currentPoint.y+"\n"+
								"getPreviousLocation().x : "+previousPoint.x+"\n"+
								"getPreviousLocation().y : "+previousPoint.y;
			}
			
		}
		
		private function onResizeHandler(e:starling.events.Event):void{
			
		}

		private function setAlignCenter(inTarget:DisplayObject):void{
			inTarget.x = stage.stageWidth >> 1;
			inTarget.y = stage.stageHeight >> 1;
		}

		public override function dispose():void {
			super.dispose();
		}
	}
}

    

설정

트랙백

댓글

Starling performance test

Project/Programming 2012. 1. 10. 21:58

package {
	import starling.events.Event;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.Button;
	import starling.textures.Texture;
	import starling.display.DisplayObject;
	import starling.display.Image;
	import starling.utils.deg2rad;
	import flash.display.Bitmap;


	public class Demo extends Sprite {

		[Embed(source = "f60.png")]
		private var MyBitmap:Class;
		
		private var _myTexture:Texture;
		private var _arrButterflys:Vector.<Butterfly>;

		public function Demo() {
			// addedToStage 이벤트에 대한 리스너 추가
			addEventListener( Event.ADDED_TO_STAGE , onAddedToStage);
		}


		private function onAddedToStage(e:Event):void {
			var myBitmap:Bitmap = new MyBitmap() as Bitmap;
			_myTexture = Texture.fromBitmap(myBitmap);

			var len:int = 800;
			
			_arrButterflys = new Vector.<Butterfly>(len, false);
			
			for (var i:int = 0; i<len; i++) {
				var fly:Butterfly = new Butterfly(_myTexture);
				
				fly.alpha = Math.random();
				fly.destX = Math.random()*stage.stageWidth;
				fly.destY = Math.random()*stage.stageHeight;
				
				fly.setVertexColor(0, Math.random()*0xFFFFFF);
				fly.setVertexColor(1, Math.random()*0xFFFFFF);
				fly.setVertexColor(2, Math.random()*0xFFFFFF);
				fly.setVertexColor(3, Math.random()*0xFFFFFF);
				
				fly.x = Math.random()*stage.stageWidth;
				fly.y = Math.random()*stage.stageHeight;
				fly.rotation = deg2rad(Math.random()*360);
				
				fly.pivotX = fly.width >> 1;
				fly.pivotY = fly.height >> 1;
				
				_arrButterflys[i] = fly;
				addChild(fly);
			}

			stage.addEventListener(Event.ENTER_FRAME, onFrame);
		}
		
		private function onFrame(e:Event):void{

			var len:uint = _arrButterflys.length;

			for (var i:int = 0; i < len; i++){

				// move the sausages around
				var fly:Butterfly = _arrButterflys[i];
				
				fly.x -= ( fly.x - fly.destX ) * .1;
				fly.y -= ( fly.y - fly.destY ) * .1;

				if (Math.abs(fly.x - fly.destX)<1 && Math.abs(fly.y-fly.destY) < 1){
					fly.destX = Math.random()*stage.stageWidth;
					fly.destY = Math.random()*stage.stageHeight;
					fly.rotation = deg2rad(Math.random()*360);
				}
			}
		}

		public override function dispose():void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			_myTexture.dispose();
			super.dispose();
		}
	}
}

import starling.display.Image;
import starling.textures.Texture;

class Butterfly extends Image{
	
	public var destX:Number = 0;
	public var destY:Number = 0;
	
	public function Butterfly(inTexture:Texture):void{
		super(inTexture);
	}
}

    

설정

트랙백

댓글

[Starling-10] 터치 이벤트와 Touch 클래스

Programming/Framework 2012. 1. 10. 14:39
Starling은 손가락의 조작이나 마우스 조작 모두 동일한 이벤트( TouchEvent )로 취급한다. Flash Player의 마우스 이벤트와는 달리 이벤트 종류는 TouchEvent.TOUCH 하나 뿐이다. 따라서 클릭 및 이동 등의 조작 상태는 이벤트에 포함되어 있는 Touch 객체 속성에서 판단하게 되어 있다.

Starling의 모든 표시 객체(Sprite, Stage, Quad, …)는 TouchEvent을 처리할 수 있다. 단, Button 클래스는 내부적으로 터치 이벤트를 triggered 이벤트로 변환하고 있기 때문에 자신의 리스너를 추가하는 것은 주의가 필요하다. 또한 표시 객체 자신이나 부모 개체 touchable 속성이 false의 경우는 TouchEvent를 받을 수 없다.

아무튼 Starling에서 터치 작업을 처리하려면 우선 표시 객체에 TouchEvent.TOUCH 이벤트 리스너를 추가해야 한다. 아래는 샘플이다.

import starling.events.TouchEvent;
myImage.addEventListener(TouchEvent.TOUCH,onTouch);

private function onTouch(e:TouchEvent):void {
trace(e.target);
}

Starling의 이벤트는 표시 목록에 버블링하기 위해 아래의 계층 객체에서 이벤트도 검색할 수 있다. (참고:사양에서는 계층의 위에서 아래로 전파하지만 지금의 구현에서는 Stage에서 이벤트를 받을 수도 있는 것 같다.)

샘플에서 사용하는 target 속​​성과 currentTarget 속​​성 사용법은 Flash Player 표준 이벤트와 동일하다. 기타 TouchEvent에는 다음과 같은 속성도 있다. 모든 읽기 전용이다.

ctrlKey : 이벤트가 발생할 때 Ctrl 키가 눌리고 있었는지 나타내는
shiftKey : 이벤트 발생시 Shift 키가 눌리고 있었는지 나타내는
timestamp : 터치가 시작된 이후의 시간(초)
touches : 사용 가능한 Touch 객체의 벡터

마지막 touches은 이벤트와 관련된 하나 이상의 Touch 객체의 벡터다. Touch 객체는 이벤트 발생시킨 손가락이나 마우스 조작 정보를 보유하고 있다.


Touch 객체 가져오기
TouchEvent에는 Touch 객체를 취득하기 위한 메소드가 포함되어 있다. 멀티 터치를 사용하지 않는 경우 getTouch()메소드에서 Touch 객체를 얻을 수 있다. (멀티 터치의 경우는 후술)

private function onTouch(e:TouchEvent):void {
var touch:Touch = e.getTouch(stage);
if (touch) {
  trace(e.target, touch.target);
}
}

getTouch() 메소드의 첫 번째 인수는 검색할 Touch 객체를 좁히려는 조건이다. 작업의 대상이 된 표시 객체가 인수로 지정된 오브젝트와 일치하는 또는 인수 계층 아래에​​ 포함된 경우, getTouch()는 값을 반환한다. 실제 작업의 대상은 Touch 객체 취득 후에 target 속​​성에서 볼 수 있다. (작업의 대상과 이벤트의 대상은 반드시 일치하지 않는다는 것에 유의)

Touch객체는 target 이외에도 작업을 식별하는 특성이 몇 가지가 더 있다.

target : 터치 조작의 대상이 된 표시 객체
id : 터치를 식별하는 숫자
timestamp : 터치 조작 시간 (터치 시작으로부터 초)


TouchPhase
위에서 이야기한 것처럼, Touch 객체는 작업 상태를 나타내는 속성이 있다. 속성 이름은 phase다.

phase : 현재 터치 상황

phase 속성에 설정 가능한 값은 TouchPhases 클래스에 정의되어 있다. 자주 사용되는 값은 다음 세 가지다.

BEGAN : 손가락이 화면에 접한 또는 마우스 버튼이 눌러진
MOVED : 손가락이 화면을 이동하거나 마우스 버튼이 눌러진 상태 이동
ENDED : 손가락이 화면에서 떨어진 또는 마우스 버튼에서 떨어진

BEGAN이 한 번, MOVED가 0 이상 반복되고, ENDED가 한 번 발생하는 것이 기본적인 흐름이다.다음과 같은 상태도 있다.

STATIONARY : 손가락 또는 마우스가 이전 프레임에서 이동

일반적으로 이 상태에서는 이벤트가 발생되지 않아야하지만, 멀티 터치를 취급하는 경우에는 STATIONARY가 포함될 수 있다.마지막으로 마우스 이용 때만 나타나는 상태다.

HOVER : 마우스 버튼이 눌러지지 않은 상태에서 표시 객체에 포인터가 있을 때

손가락으로 (지금까지) 접하지 않고 포인터를 이동시킬 수 없기 때문에 OVER는 있어도 HOVER는 없다는 것이다.


포인터 위치 가져오기
Touch 객체는 스테이지의 현재 위치와 이전 위치 속성을 가지고 있다. 다음 네 가지다. 형태는 모두 Number.

globalX : 스테이지 좌표를 기준으로 한 현재의 손가락 (포인터)의 x 좌표
globalY : 스테이지 좌표를 기준으로 한 현재의 손가락 (포인터)의 y 좌표
previousGlobalX : 스테이지 좌표를 기준으로 이전의 손가락 (포인터)의 x 좌표
previousGlobalY : 스테이지 좌표를 기준으로 이전의 손가락 (포인터)의 y 좌표

그러나 대부분의 경우 다른 좌표계에서의 위치를​​ 계산하게 될 것이다. 그래서, Touch 클래스에는 위의 속성 값을 특정 객체를 기준으로 한 좌표로 변환하는 메소드를 제공 한다.

public function getLocation(space:DisplayObject):Point
public function getPreviousLocation(space:DisplayObject):Point

이 두 메소드는 인수 객체의 좌표를 기준으로 한 현재의 위치 또는 마지막 위치를 반환한다.아래는 Touch 객체의 위치 정보를 사용하는 샘플이다.

var touch:Touch = event.getTouch(this, TouchPhase.MOVED);
if (touch) {
// 부모 개체를 기준으로 이동 거리를 계산하는
var currentPos:Point = touch.getLocation(parent);
var previousPos:Point = touch.getPreviousLocation(parent);
var delta:Point = currentPos.subtract(previousPos);

// 객체의 위치를​​ 변경
x += delta.x;
y += delta.y;
}

getTouch() 메소드의 두 번째 인수는 원하는 상태를 지정할 수 있다. 위의 예제에서는 손가락 이동 정보를 취급하기 위하여 TouchPhase.MOVED를 지정하고 있다.


더블 클릭의 사용 방법
Touch 객체를 통해 더블 클릭을 감지할 수 있도록 tapCount 속성이 있다. 이 속성을 사용하여 더블 클릭에 해당하는 코드를 작성한 것이 아래의 예제다.

touch = event.getTouch(this, TouchPhase.ENDED);
if (touch & & touch.tapCount == 2) parent.addChild (this);

작동 상태가 ENDED의 Touch 객체가 존재하며 탭 횟수가 2 회인 경우를 검색하고 있다.멀티 터치Starling은 멀티 터치도 지원한다. 멀티 터치를 처리하려면 Starling 객체에 다음과 같이 설정한다.

mStarling.simulateMultitouch = true;

이 상태에서 실제로 멀티 터치를 하면 터치 때마다 Touch 객체가 만들어진다. 그리고 TouchEvent에 전달된다. TouchEvent에서 여러 Touch 개체를 가져오려면 getTouches() 메소드를 사용한다. 아래는 그 예제다.

var touches:Vector.<Touch>=touchEvent.getTouches(this); 


반환 값은 Touch 벡터다. getTouch()뿐만 아니라 두 번째 인수로 원하는 상태를 지정할 수 있다.참고로 Ctrl 키를 사용하여 데스크탑 환경에서도 멀티 터치를 시뮬레이션할 수 있게 되어 있다. 물론 실제 터치 디바이스에서는 손가락으로 조작하면 된다.

원본 : http://cuaoar.jp/2012/01/starling-touch.html



package {
	import starling.events.Event;
	import starling.events.Touch;
	import starling.events.TouchEvent;
	import starling.events.TouchPhase;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.Image;
	import starling.textures.Texture;
	import starling.display.DisplayObject;
	import starling.animation.Tween;
	import starling.animation.Transitions;
	import starling.utils.deg2rad;
	import flash.display.Bitmap;
	import flash.geom.Point;


	public class Demo extends Sprite {

		[Embed(source="icon128.png")]
		private var MyBitmap:Class;
		
		private var myButton:Image;

		public function Demo() {
			// addedToStage 이벤트에 대한 리스너 추가
			addEventListener( Event.ADDED_TO_STAGE , onAddedToStage);
		}


		private function onAddedToStage(e:Event):void {
			var myBitmap:Bitmap = new MyBitmap() as Bitmap;
			var myTexture:Texture = Texture.fromBitmap(myBitmap);
			
			myButton = new Image(myTexture);
			myButton.addEventListener(TouchEvent.TOUCH, onTouchHandler);
			
			myButton.pivotX = myButton.width >> 1;
			myButton.pivotY = myButton.height >> 1;
			addChild(myButton);
			setAlign(myButton);

		}
		
		private function onTouchHandler(e:TouchEvent):void{
			var touch:Touch = e.getTouch(myButton);
			if(touch){
				switch(touch.phase){
					case TouchPhase.BEGAN : myButton.setVertexColor(0, 0xFF6600);
											myButton.setVertexColor(1, 0xFF6600);
											myButton.setVertexColor(2, 0xFF6600);
											myButton.setVertexColor(3, 0xFF6600);
					break;
					case TouchPhase.MOVED : 
					break;
					case TouchPhase.ENDED : myButton.setVertexColor(0, 0xFFFFFF);
											myButton.setVertexColor(1, 0xFFFFFF);
											myButton.setVertexColor(2, 0xFFFFFF);
											myButton.setVertexColor(3, 0xFFFFFF);
				}
			}
			
			
			/*
			touch = e.getTouch(myButton, TouchPhase.MOVED);   
			if (touch) {        
					// 부모 개체를 기준으로 이동 거리를 계산하는      
					 var currentPos:Point = touch.getLocation(parent);      
					 var previousPos:Point = touch.getPreviousLocation(parent);     
					 var delta:Point = currentPos.subtract(previousPos);        
			 
					// 객체의 위치를​​ 변경     
					 myButton.x +=  delta.x;     
					 myButton.y +=  delta.y; 
			}
			*/
			
			touch = e.getTouch(this, TouchPhase.ENDED); 
			if (touch && touch.tapCount == 2){
				rotationToTween(0.6, 90, Transitions.EASE_OUT_BOUNCE);
			}
			
			var touches:Vector.<Touch> = e.getTouches(stage); 
			if(touches.length == 2){
				var firstPos:Point = touches[0].getLocation(parent);      
				var secondPos:Point = touches[1].getLocation(parent);  
				var distance:Number = Point.distance(firstPos, secondPos);

				scaleToTween(0.2, distance*0.01, Transitions.EASE_OUT);
			}else{
				touch = e.getTouch(this, TouchPhase.MOVED); 
				if (touch){
					moveToTween(0.14, touch.globalX, touch.globalY, Transitions.EASE_OUT);
				}
			}
			
		}
		
		private function rotationToTween(inTime:Number, inRot:int, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.animate("rotation", myButton.rotation+deg2rad(inRot));
			Starling.juggler.add(tween);
		}
		
		private function moveToTween(inTime:Number, inX:int, inY:int, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.moveTo(inX, inY);
			Starling.juggler.add(tween);
		}
		
		private function scaleToTween(inTime:Number, inScale:Number, inEase:String):void{
			var tween:Tween = new Tween(myButton, inTime, inEase);
			tween.scaleTo(inScale);
			Starling.juggler.add(tween);
		}

		private function setAlign(inTarget:DisplayObject):void {
			inTarget.x = stage.stageWidth >> 1;
			inTarget.y = stage.stageHeight >> 1;
		}

		public override function dispose():void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			super.dispose();
		}
	}
}

    

설정

트랙백

댓글

[Starling-09] Starling의 Button 클래스 사용

Programming/Framework 2012. 1. 9. 11:22
Starling은 버튼을 쉽게 구현할 수 있도록 Button 클래스가 포함되어 있다. Button 클래스는 DisplayObjectContainer 클래스의 서브 클래스다. 따라서 모든 표시 객체를 자식으로 가질 수 있지만 보통은 스킨 이미지와 제목을 표시한다. 그리고 버튼 클릭을 알리는 기능이 있다.

아래가 Button의 생성자다. 첫 번째 인수는 버튼에 대한 스킨이 되는 텍스처이고 옵션으로, 두 번째 인수에 제목을, 3번 째 인수에는 누를 때 표시할 텍스처를 지정할 수 있다.
Button(upState:Texture, text:String = "", downState:Texture=null)

Flash Professional에서 만든 버튼과 달리, "오버" 스킨은 지정할 수 없다. 아마도 이것은 터치 장치의 이용을 전제로 하고 있기 때문이라고 생각된다.

버튼을 클릭하면 (또는 탭하면) triggered라는 이벤트가 발생한다. 클릭시 실행하고 싶은 처리는 triggered 이벤트 리스너 함수에 작성한다. 아래는 그 예제다. 텍스처 포함 관련 부분은 여러번 나왔기 때문에 생략한다.
import starling.display.Button;
	import starling.events.Event;

	private var myButton:Button;

	private function onAddedToStage(e:Event):void {
		var myBitmap:Bitmap = new MyBitmap  ;
		var myTexture:Texture = Texture.fromBitmap(myBitmap);

		// 텍스처를 지정하여 단추의 객체를 생성
		myButton = new Button(myTexture);

		// triggered 이벤트 수신기를 추가
		myButton.addEventListener(Event.TRIGgered, onTriggered);
	}

	private function onTriggered(e:Event):void {
		// 이벤트를 발생 개체를 가져
		trace(e.target);
	}

Staring 이벤트는 Flash Player의 이벤트처럼 버블링(표시 목록 계층 간에 전파, 자식에서 부모 방향으로만 지원) 하는 여러 버튼을 사용할 때는 triggered 이벤트를 부모 객체에서 처리하는 것도 가능하다.


스킨 지정
Starling 버튼은 터치하면 (또는 버튼에 마우스가 눌러진 상태가 되면), 버튼의 표시 크기가 조금 작아진다. 손가락을 떼면 원래 크기로 돌아간다. 이 것은 버튼에 표준 텍스처 하나만 지정된 경우의 동작이다. 버튼을 눌린 상태에 해당하는 텍스처를 지정하면 텍스처가 작아지는 대신 스킨이 바뀐다.

이 두 텍스처는 생성자에 지정할 수 있지만 나중에 속성에서도 설정할 수 있다. 아래가 속성 목록이다.

upState : 버튼이 터치되지 않은 상태로 표시되는 텍스처
downState : 버튼이 터치되는 상태로 표시되는 텍스처
scaleWhenDown : 터치되어있는 상태 텍스처의 표시 비율

upState과 downState가 각 상태로 표시하는 텍스처를 설정하는 속성이다. (upState는 생성자에서 지정 필수) 성능의 관점에서 어느 텍스처도 같은 텍스처 아틀라스에 있는 것이 바람직할 것이다. 마지막 scaleWhenDown는 0에서 1 사이의 숫자를 지정한다. 이 값은 downState = null의 경우에만 사용할 수 있다.

위에서도 썼지만, 단순한 장식 추가가 필요한 경우, 2 개의 텍스처 이외에도 항시 표시 객체를 추가할 수 있다. 만약 동작을 변경하려면, 커스텀 버튼 클래스를 만드는 편이 빠를지도 모른다.


텍스트 표시
Starling의 버튼은 텍스처로 지정한 이미지에 텍스트를 표시할 수 있다. 텍스트는 생성자에서 지정하거나 인스턴스 생성 후, text 속성에 설정한다. textBounds 속성을 사용하여 텍스트의 위치를​​ 지정할 수도 있다.

text : 버튼에 표시되는 텍스트
textBounds : 텍스트 표시 영역을 Rectangle로 지정

텍스트 표시에 사용할 글꼴의 모양을 설정하는 속성도 있다. 거의 TextField의 경우와 동일하다.

fontName : 텍스트 표시에 사용할 글꼴 이름
fontSize : 텍스트 표시에 사용할 글꼴의 크기
fontColor : 글꼴 색상
fontBold : 글꼴을 굵게 표시 여부를 지정

포함된 글꼴과 비트맵 글꼴을 사용할 수 있다.


버튼 비활성화
버튼을 사용할 수 없는 상태라면 enabled 속성의 값을 false로 적용한다.

enabled : 버튼을 사용할 수 있는지 여부를 지정
alphaWhenDisabled : 버튼이 비활성화 때 알파 값

기본적으로 단추를 비활성화하면 버튼의 표시가 반투명하게 된다. 그 때 사용되는 알파 값은 alphaWhenDisabled에 설정된 값이다.


원본 : http://cuaoar.jp/2012/01/starling-button.html




package {
	import starling.events.Event;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.display.Button;
	import starling.textures.Texture;
	import starling.display.DisplayObject;
	import flash.geom.Rectangle;
	import flash.display.Bitmap;

	public class Demo extends Sprite {

		[Embed(source="icon128.png")]
		private var MyBitmap:Class;
		
		private var myButton:Button;

		public function Demo() {
			// addedToStage 이벤트에 대한 리스너 추가
			addEventListener( Event.ADDED_TO_STAGE , onAddedToStage);
		}


		private function onAddedToStage(e:Event):void {
			var myBitmap:Bitmap = new MyBitmap() as Bitmap;
			var myTexture:Texture = Texture.fromBitmap(myBitmap);

			// 텍스처를 지정하여 단추의 객체를 생성
			myButton = new Button(myTexture);
			
			// 터치 상태일 때, 텍스처의 표시 비율
			myButton.scaleWhenDown = 0.95;
			
			myButton.fontColor = 0xFFFFFF;
			myButton.fontBold = true;
			myButton.fontSize = 24;
			myButton.text = "BUTTON";
			myButton.textBounds = new Rectangle(myButton.width-60>>1, 85, 60, 30);
			
			// 
			addChild(myButton);
			setAlign(myButton);
			
			// triggered 이벤트 수신기를 추가
			myButton.addEventListener(Event.TRIGGERED, onTriggered);
		}

		private function onTriggered(e:Event):void {
			// 이벤트를 발생 개체를 가져
			trace(e.target);
			myButton.fontColor = Math.random()*0xFFFFFF;
		}

		private function setAlign(inTarget:DisplayObject):void {
			inTarget.x = stage.stageWidth - inTarget.width >> 1;
			inTarget.y = stage.stageHeight - inTarget.height >> 1;
		}

		public override function dispose():void {
			removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
			super.dispose();
		}
	}
}

    

설정

트랙백

댓글