검색결과 리스트
로그의 노트에 해당되는 글 212건
- 2013.03.31 [Android] 안드로이드앱 개발 도전기 (2)
- 2013.02.16 [iPhone] 아이폰 앱 디자인 실수: 문맥에 대한 간과
- 2013.02.16 [Java] 자바란?
- 2013.02.03 [java] 라이프 게임 (life game) (1)
- 2013.01.31 [연말정산] 월세공제 확정일자에 대한 잘못된 진실 (2)
- 2013.01.30 [java] 제13회 한국자바개발자 컨퍼런스
- 2013.01.25 [iPhone] iOS6 회전 지원하기
- 2013.01.23 [iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation)
- 2013.01.22 [Mask 레이어 활용] 이미지 일부만 보여주기 (2)
- 2012.11.12 2011년 스마트폰 사용자 통계
글
[Android] 안드로이드앱 개발 도전기
LifeGame 안드로이드 앱개발 도전기!
전혀 변화가 없는 고정된 패턴(정물, still life),
일정한 행동을 주기적으로 반복하는 패턴 (진동자, oscillator),
한쪽 방향으로 계속 전진하는 패턴(우주선, spaceship).
<메인 인터페이스>
<패턴을 로드하고 저장하는 기능>
<The Game of Life 소개>
<게임규칙 변경>
물론 마켓에 올렸다고 끝이 아닌것 잘 알고 있다. 관심을 가져주는 분들이 있고,
개선방향에 의견을 주시면 언제든지 반영할 준비가 되어있다.
(수고했다는 의미로 광고한번 살포시 눌러주면 개발자에게 큰힘이 된답니다 ^^)
자. 이제 더 흥미로운 앱을 개발하기 위해 다시 고민할 때이다!
'앱개발로그' 카테고리의 다른 글
| [Android] 안드로이드앱 개발 도전기 (2) | 2013.03.31 |
|---|---|
| 2011년 스마트폰 사용자 통계 (0) | 2012.11.12 |
| BlackBerry Architecture (0) | 2011.04.26 |
설정
트랙백
댓글
글
[iPhone] 아이폰 앱 디자인 실수: 문맥에 대한 간과
Smashing Magazine 사이트에 아이폰앱 디자인시 범하는 실수에 대한 글이 있어 번역해보았습니다. 좋은 내용이네요 ^^b
(발번역이지만 이해해 주세요 ^^)
원문: iPhone Apps Design Mistakes : Disregard Of Context by Alex Komarov
아이폰 앱들의 디자인 실수 : 컨텍스트를 간과한다.
- 아이폰은 훨씬 큰 그림의 일부일 뿐이다. 인간과 환경요소들 얼마나 잘 풀어내는냐가 앱의 성공요소를 결정한다.
너무나 자주 아이폰 개발자들은 고객과는 동떨어진 제품을 만들어낸다.
- 정말로 호소력있는 앱을 창조하기 위해서는, 앱의 메커니즘에만 집중하는 행위를 멈춰라. 멀리떨어져서 보아라.
- 사람을 감싸고 있는 복잡한 환경적인 요소들 뿐만 아니라 애플리케이션을 사용하는 사람들을 이해하라.
- 이러한 디자인 과제들의 문맥을 더 잘 이해하기 위해서 우리는 몇몇 수준에서 인간과 환경적인 요소들에 주목할 것이다.
레벨1, 당신은 고객들이 사랑하는 앱을 만들기 위해 여기에 있다.
멀리 떨어져서 보아라
레벨1 - 앱자체
- 많은 개발자들이 그들의 앱을 바라보는 관점이다. 당신의 앱이 어떻게 보여야 하고, 왜 고객들이 그것에 관심을
주목할 것인지에 대한 비전을 가지고 있다. 그러나 제품에만 몰두하여 바라본다면 잘못된 문맥 속에 놓고
잘못된 목적과 잘못된 사용자들을 위해 설계하게 될것이다.
- 이것이 멀리 떨어져서 보아야 하는 이유이다.
레벨2, 한명의 사람이 이 앱을 사용하고 있다.
- 그 사용자는 특정한 목적과 과제를 가지고 있다. : 이 부분에서 가장 두드러지고 가장 무시되는 몇가지에 대해서 알아볼것이다.
- 아이폰에 존재하는 인간적인 요소들. 기본적인 인체공학과 시각적인 한계, 공통적인 디자인 실수에 대해서 이야기 할 것이다.
레벨 3. 그 사람은 특정한 환경속에서 이 앱을 사용하고 있다.
- 뒤로 물러나라. 그러면 이 앱은 복잡한 사회 환경속에 일부라는 것을 보게 될것이다. 앱은 사람들 사이의 커뮤니케이션하고,
사람들이 더 큰 목적을 달성하는 것을 돕는 데에 있어서 상대적으로 적은 역할을 맡고 있다.
- 이곳이 사회적 컴포넌들이 동작하는 장소이다. 네트워킹과 커뮤니티, 소셜 기반의 웹사이트와 같은…
- 애플리케이션들과 많은 다른 것들이 환경과 문맥을 창조하는…그 환경속에서 애플리케이션들은 사용되어질 것이다.
레벨4. 환경은 더 큰 문화의 일부이다.
- 다른 문화들간에 독특한 니즈를 해결하는 당신의 능력이 제품의 성공에 영향을 미칠것이다.
그것을 무시하는 것은 너무 비용이 크며, 만일 앱이 세계적으로 팔릴 것이라면 특히 더 그렇다.
환경은 글로벌 네트워킹의 일부임을 이해하는 것이 중요하다.
- 지역문화 뿐만이 아니라 세계적인 성공을 얻을 앱을 창조하기 위해서는 문화적인 차이, 트렌드, 메타포를 인식해야 한다.
레벨2, 사람의 니즈와 한계를 이해하라.
"두번 측정하고, 한번 잘라라" 는 실제로도 효과적인 전략이다. 아이폰 개발자로서 한걸음 뒤로 물러나 코딩을 시작하기 전에
아래와 같은 질문들에 답할 수 있어야 한다.
- 누가 당신의 애플리케이션들을 사용할 것인가?
- 그 사용자의 기능요구사항은 무엇인가?
- 그 사람의 제한(한계)는 무엇인가?
이 질문에 대한 답들이 당신의 관점을 넓혀주고, 당신 고객의 니즈를 해결하는데 도움을 줄것이다.
모든 인간적인 요소 전문인들은 단지 이것에 전념한다.
기본적인 인체공학
- 아이폰과 관련하여 물리적, 인지적, 인체공하적인 진실들이 몇가지 있다.
1. 우리의 손가락들은 마우스 포인터가 아니다.
- 대다수의 탭가능한 객체들이 너무 작아서 사용하기를 좌절시키는 인터페이스를 사용한다.
- 구글맵앱보다 2배 적은 핀들을 사용해서, 태핑이 매우 어렵다.(iFitness) 손가락이 3개 이상의 핀영역을 차지하기 때문에..
- 결국 영역을 반복해서 태핑하게 되고 랜덤으로 핀이 활성화되어 원치 않는 결과를 보게 된다.
인체공학적인 문제들을 해결하기 위한 몇가지 방법이 있다.
1) 버튼과 다른 탭가능한 객체들을 더크게 만들어라.
2) 더크게 만드는 것이 불가능하다면 버튼 그 자체보다 더 크게 클릭 가능하게 영역을 확장해라
3) 각 화면에서 옵션의 수를 줄이고, 선택의 프로세스를 순차적이게 만들어라.
4) 인터페이스 안에서 다중 터치 제스처를 구현해라.
예를 들자면, 두손가락 줌제스처를 사용한것이 머슬 그룹을 더 쉽게 선택할 수 있게 해줄것이다.
2. 불행하게도 우리는 수퍼 영웅이 아니다.
- 앱 디자이너들은 시각적인 제약사항들에 대해 고려해야 한다. 모바일 폰들은 일반적으로 컴퓨터보다 조명 조건이
좋지 않은 장소에서 사용되어진다.
- 덜컹거리는 버스 기차에서, 햇빛 비추는 거리에서 걸으면서 앱을 사용하는 사람들을 생각해보아라.
기술이 유용하고, 완변하게 실행될 지라도 사람들은 무슨 일이 벌어지는지 알기가 힘들어진다면 앱 사용을 꺼리게 될것이다.
- 여기 몇가지 시각적인 한계를 고려하지 않은 잠재적으로만 유용한 앱이 몇가지 있다.
[너무 어무운 색만을 사용해서 앱을 디자인 하지 말아라.]
TweetDeck
여기 몇가지 실수를 피할수 있는 방법들이 있다.
1) 반드시 필요한 요소들만 선택해라. 그것을 더 크게 만들고, 다른 모든것들은 제거하라.
만일 필요하다면 더 많은 옵션을 위해 추가적인 화면을 만들어라. [화면에 덜 필수적인 아이콘들은 제거하라.]
2) 아이폰에서의 픽셀 화면 크기는 컴퓨터 화면보다 작다는 것을 기억해라.
시뮬레이터에서 보는 스크린샷은 아이폰에서는 실제로 더 작다.
레벨3, 사용자의 환경에 특화된 문제(과업)들을 이해하라.
목적과 환경
- 당신의 앱은 사용자가 더 큰 목적 달성을 돕는데 상대적으로 더 적은 역할을 한다.
- 사람들이 어떤 목적을 가지고 있고 그들이 그것들을 달성하기 위해 필요한 것을 더 잘 이해할 수록,
당신은 그들의 니즈를 더 많이 만족하는 앱을 만들수 있다.
- 모바일폰은 종종 시끄럽고 집중을 저해하는 환경속에서 사용된다.
도시를 통과하는 간단한 산책만 하더라도 시끄러운 요소들이 많다.
- 다음 예제를 보아라. 어떤 목소리 메모앱이 더 역할을 잘 할 것 같은가? (Apple Voice vs iTalk)
애플의 보이스 메모가 더 멋져 보이지만, iTalk가 평균적인 사용자들의 목적과 환경을 더 잘 해결하고 있다.
생각해 보아라. 왜 사람들은 노트를 쓰는 것보다 목소리를 녹음하는 것을 더 선호하는가?
오디오 형식은 단순한 텍스트보다 장점이 거의 없다. 검색을 할수도 없고, 텍스트 만큼 쉽게 수정과 보완을 할수 없다.
대부분의 시나리오에서는 정보를 교환하는 관점에서 텍스트가 훨씬 더 편리한 형식이다.
그렇다면 왜… 더 중요하게는 언제 사람들은 목소리 메모를 사용하는가? 언제 그들이 타이핑 할 수 없는가?
가장 흔한 때는 운전하고 있을 때이다. 운전하면서 타이핑 하는 것이 그렇지 않는 경우보다 사고의 위험이 23배 높다고 한다.
이러한 경우에 어떤 앱이 더 사용하기 쉬운가? 크고 빛나는 마이크와 작은 녹음 버튼을 가진 앱은 누르기 불편하지 않을까?
반면 화면 절반 크기의 녹음 버튼을 가진 것은? 당연히 후자이다.
사용자가 녹음이 활성화 됐다는 것을 확인하게 하는 것도 역시 중요하다. 어떤 인터페이스가 디바이스 상태를 더 잘 알리고 있는가? 당신이 녹음을 완료하고 싶을 때는 어디를 택해야 하는가?
전박적으로 어떤 지다인이 더 잘 동작하는 가에 기초하면, iTalk의 승리이다. 애플 보이스 메모는 친구의 폰을 구경할때는
멋져보이지만 실제환경에서는 유용하지 않다.
모바일 폰들, 네트워킹과 커뮤니티
- 의심의 여지없이 모바일폰은 소셜 도구 중에 하나이다. 머 많은 사람들을 관련시킬수록 더 많은 경험들이 발생한다.
생각해보아라. 오직 하나의 폰만 가지고 있다면, 별로 유용하지 않을 것이다. 유투브, 페이스북, 트위터는 우리들은 사회적인
존재임을 이해하고 만들어진 서비스이다.
- 우리는 공유하기를 원한다. 사회적인 인터렉션을 위탁한 설계가 얼마나 극적으로 모바일 세계를 변화시키는지 상상해보아라.
외견상으로 보이는 정보를 공유하고 획득하는 반복적인 방법들에 사람들은 압도당한다. 그것들에 대처하기 위해서 디자이너들은
애플리케이션을 가능한한 효과적으로 만들기 위해 아이폰 플랫폼을 잘 이해해야 한다.
Bump : 단순히 부딪히는 제스터를 통해 연락처 정보를 교환함.
Mover : 쉽게 정보(사진?)을 전달 할 수 있음.
Loopt : 내 주변에 있는 사용자들을 알 수 있음.
레벨4. 환경은 거대한 문화의 일부이다.
다른 문화간의 독특한 니즈를 해결하는 당신의 능력이 당신 앱의 성공에 영향을 미칠 것이다. 그것들을 무시하는 것은 비용이
너무 크고, 특히 앱을 세계를 대상으로 판매할 경우 더욱 그렇다. 지다인은 지역적인 문제들에 적응되야 한다.
사용성 전문가 제이콥 닐슨은 이렇게 이야기 했다.
"ATM 기 전에는 왜 그렇게 큰 버튼을 가졌는지 이해하지 못했다. 2월의 스톡홀름에서 즉시 이해할 수 있었다.
두꺼운 장갑을 끼고도 누르는 것이 가능하다."
시스템은 사용자의 문화적인 특성에 부합해야 한다. 이것은 공격적인 아이콘을 피하는 것 이상의 의미를 갖는다.
비지니스가 수행되는 방식을 수용하고, 다양한 나라에서 사람들이 소통하는 방식을 이해해야 한다.
결론
훌륭한 앱을 디자인 하는것은 쉬운 일이 아니다.
하지만 사용자의 니즈를 이해하려는 당신은 노력은 보상받을 것이다.
'아이폰' 카테고리의 다른 글
| [iOS] 익스텐션 (Extension) (0) | 2015.08.18 |
|---|---|
| [iPhone] 아이폰앱 디자인 정석(TapWorthy) (1) | 2013.07.07 |
| [iPhone] 아이폰 앱 디자인 실수: 문맥에 대한 간과 (0) | 2013.02.16 |
| [iPhone] iOS6 회전 지원하기 (0) | 2013.01.25 |
| [iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation) (0) | 2013.01.23 |
| [Mask 레이어 활용] 이미지 일부만 보여주기 (2) | 2013.01.22 |
설정
트랙백
댓글
글
[Java] 자바란?
벌써 4년전입니다. 2009년도에 비트 자바고급반을 수강하면서 학습한 내용을 Springnote에 꾸준히
정리했습니다. Springnote 서비스가 종료되고, 그 때 백업해 두었던 글들을 하나씩 티스토리로 옮기고
있는데... 열정과 흥미를 가지고 재미있게 공부했었던 그때 기억이 나네요.
2. 자바란?
자바 언어의 개발사인 썬은 spac 이란 서버제품군을 제작하는 회사였습니다.
낮은 H/W 마진을 개선하고자, 자사의 H/W 위에 마진이 높은 S/W(유닉스 등)를 얹혀서 팔려했는데
하드웨어마다 소프트웨어를 재작성해야 하는 문제가 있었습니다. 즉 다양한 머신 위에서
한번작성하면 동일하게 동작하는 S/W를 만들려는 노력해서 Java가 탄생하게 되었습니다
위키: http://en.wikipedia.org/wiki/Java_language
3. 자바의 장점
객체지향적, 플랫폼 독립적, 동적이고 배포하기 쉬우며... 견고하고 보안에 강하다!
하지만 객체지향적이고, 보안에 강하다는 특성을 살리는 것은 소프트웨어를 작성하는 개발자에 전적으로
달려있답니다. (무서운 강사님의 말씀...)
4. 플랫폼 독립적?
c언어는 컴파일러가 머신에 의존적인 기계어 코드(실행파일)를 생성합니다. 유닉스, 리눅스, 윈도우, 맥에서
실행하기 위해서는 소스 수준에서 재컴파일이 필요합니다.
자바는 OS와 소스코드 중간에 JVM(Java Virtual Machine)이라는 중간계층을 두어 이를 해결했습니다.
소스코드를 가산머신이 인식할 수 있는 중간언어인 바이트 코드로 컴파일해서 배포합니다. 그러면 각 OS에
설치되어 있는 JVM이 바이트 코드를 기계어로 번역하여 실행합니다.
(단, 각 OS 마다 각 머신에 맞는 JVM이 설치되어 있어야 합니다. 한번만 설치하면 되니까 매번 소스를
재컴파일해야 하는 것보다는 훨씬 유연성이 있죠)
5. 기업 인터넷 환경에서의 Java
유연성을 위해 3-Tier(프레젠테이션 계층-비지니스 계층-데이터(DB) 계층)의 형태로 구성됩니다.
현재는 더욱 확장된 N-Tier로 구성된다고 하네요.
PS. Three Tier를 삼-Tier로 읽는 센스 ㅋ
6. JVM 의 동작방식
클래스 로더가 소스코드를 스캔하여 모든 필요한 클래스를 로드합니다. 필요한 클래스 위치는 CLASSPATH
환경변수를 참조하게 찾게됩니다. JVM Verifier 가 오작동 될 수 있는 코드(무한루프와 같은...)를 검사해서
사전에 차단합니다. 이어서 바이트 코드를 실행하게 되는데, 이때 JIT(Just-In-Time) 컴파일러가 동작합니다.
즉 소스코드와 (컴파일된 .class)클래스 파일의 수정일자를 비교해서, 소스코드가 더 나중에 수정됐을 경우에만
재컴파일하게 됩니다.
(PS. JIT를 도입해서 자바의 느린 성능을 어느정도 극복했고, 버전업 될때마다 크게 향상되고 있습니다.
또 가비지 컬렉터라는 메모리 관리자가 더이상 사용되지 않는 객체를 수시로 검사하여 자동으로 메모리를
반환해줍니다)
7. 자바환경에서 보안 구현
언어차원과 컴파일러, 클래스 로더, 바이트코드 검증기(Verifier) 등 계층적으로 위해한 코드를 차단해줍니다.
8. 자바 애플리케이션의 배포방식
.zip 또는 .jar로 압축해서 배포합니다. .jar은 단순히 소스만 압축할 수도 있고, 클래스파일까지 함께 압축해서
실행가능한 형태로 만들 수 있습니다. 웹에서는 .war 형태로 압축해서 배포됩니다.
9. JDK(Java Development Kit)
통상 J2SE를 지칭합니다.컴파일러, 코어클래스 라이브러리, 디버거, JVM, JAR(Java ARchive utility) 등으로
구성됩니다. Java 1.2버전 이후부터 대폭 내부구조가 개선되었는데 이를 표현하기 위해 1.2버전 이후부터는
JAVA2라고 부릅니다. (웹2.0 처럼 큰 변화가 있었다는 의미이지요).
JAVA2에는 J2SE, J2ME, J2EE 이렇게 3가지가 있습니다.
J2ME는 모바일 환경의 S/W 작성을 위해 필요한 클래스만 추려낸 버전이고, J2EE는 기업환겨에 맞게 병렬실행
등을 강화한 버전입니다.
10. 자바빈즈(JavaBeans)
Beans는 땅콩을 의미하는 땅콩은 알맹이를 껍데이가 감싸는 형태로 되어있죠.
클래스가 속성과 메소드를 깜싸고 있는 모습이 흡사 땅콩과 같다해서 자바빈즈라고 부릅니다.
보통 프레임워크에서 데이터 속성과 표준화된 getter, setter를 갖는 클래스를 빈즈라고 합니다.
11. 기본형 데이터 타입
기본형 |
크기 |
Wrapper 클래스 |
비고 |
boolean |
16비트 |
Boolean |
true, false |
char |
8비트 |
Character |
유니코드 사용 |
byte |
8비트 |
Byte |
|
short |
16비트 |
Short |
|
int |
32비트 |
Integer |
|
long |
64비트 |
Long |
|
float |
32비트 |
Float |
|
double |
64비트 |
Double |
|
'언어로그 > Java' 카테고리의 다른 글
| [java] 글 목록 (0) | 2015.09.03 |
|---|---|
| [Java] 자바란? (0) | 2013.02.16 |
| [java] 라이프 게임 (life game) (1) | 2013.02.03 |
| [java] 제13회 한국자바개발자 컨퍼런스 (0) | 2013.01.30 |
| Inner Class(내부 클래스) (0) | 2011.04.12 |
| 컬렉션(Collection) (0) | 2011.04.11 |
설정
트랙백
댓글
글
[java] 라이프 게임 (life game)
스프링노트를 운영하던 시절 썼던 글인데 티스토리로 옮기면서 다시 읽어 보니 감회가 새롭네요
요즘엔 AWT/Swing 이 거의 쓰이지 않지만 이때 공부했던 GUI 프로그래밍이 이후에 플렉스와 아이폰을
공부하면서 GUI에 적응하는데 밑거름이 되었던 것 같네요!
1. 라이프 게임 개발을 시작하다
2008년 6월 25일 수요일 새벽 잠들기 전....
1년간 프로그래밍에 전혀 손 대지 않다가... 다시 프로그래밍을 하려고 하니 영~ 힘들다
1년이란 시간이 매우 길긴 기나부다...그 많은 것을 잊어버렸다. 자바 Spring 프레임워크를 파보려고 하는데
읽어도 무슨말인지 모르겠고, 이클립스 등 개발환경 셋팅하는데만 하루가 걸렸다. ㅡㅡ;;
잊어버린 자바 관련 지식을 상기시키고자, 첫 번째 프로젝트에 들어갔다. 프로그램은 "콘웨이"에 '라이프 게임!'
게임 알고리즘이 간단해서 연습용 프로젝트에 딱인 것 같다.
그럼 라이프 게임이 무엇인지부터 알아보자!
2. 라이프 게임이란?
라이프 게임(Game of Life) 또는 생명 게임은 영국의 수학자 존 호튼 콘웨이가 고안해낸 세포 자동자의
일종으로, 가장 널리 알려진 세포 자동자 가운데 하나이다. 미국의 과학잡지 사이언티픽 어메리칸 1970년
10월호 중 마틴 가드너의 칼럼 〈Mathematical Games(수학 게임)란을 통해 처음으로 대중들에게 소개되어
단순한 규칙 몇가지로 복잡한 패턴을 만들어낼 수 있다는 점 때문에 많은 관심과 반응을 불러일으켰다.
설명
이 ‘게임’은 사실 게임을 하는 사람이 자신의 의지로 게임의 진행을 결정하는 일반적인 게임과는 다르다.
라이프 게임의 진행은 처음 입력된 초기값만으로 완전히 결정된다.
라이프 게임은 무한히 많은 사각형(혹은 ‘세포’)로 이루어진 격자 위에서 돌아간다. 각각의 세포 주위에는
인접해 있는 여덟 개의 ‘이웃 세포’가 있으며, 또 각 세포는 ‘죽어’ 있거나 ‘살아’ 있는 두가지 상태중
한가지 상태를 갖는다. 격자를 이루는 세포의 상태는 연속적이 아니라 이산적으로 변화한다. 즉, 현재 세대의
세포들 전체의 상태가 다음 세대의 세포 전체의 상태를 결정한다.
...
패턴의 예
라이프 게임에는 전혀 변화가 없는 고정된 패턴(정물 靜物, still life), 일정한 행동을 주기적으로 반복하는 패턴
(진동자, oscillator), 한쪽 방향으로 계속 전진하는 패턴(우주선, spaceship) 등 여러 패턴이 존재한다.
‘block’과 ‘boat’는 정물이고, ‘blinker’와 ‘toad’는 진동자, 그리고 ‘글라이더(glider)’와 ‘경량급 우주선(lightweight spaceship — LWSS)’은 우주선에 속한다...
출처 Wikipedia
3. 게임 규칙
게임규칙을 요약하면 아래와 같다
* 셀의 상,하,좌,우,각 대각선 8개의 인접한 셀을 이웃으로 한다.
* 셀은 세대를 거듭하며 살거나 죽는다.
1. 정확히 3개의 이웃이 살아있다면, (죽어있는) 셀이 살아난다.
2. 2개의 이웃이 살아있다면 살아있는 셀은 다음세대에도 살아남든다
3. 1개 이하 또는 4개 이상의 이웃이 살아있다면, 살아있는 셀은 외로워서 또는 질식해서 죽는다.
4. 구현하기
프로그래밍을 하기 위하여 구현한 절차는 아래와 같다.
먼저 모든 셀들을 순회하면서 살아있는 이웃셀을 카운트하고, 살아있는 이웃의 개수를 저장한다
이를 바탕으로 위 세가지 규칙을 적용하여 다음세대 살아있는 셀들을 결정한다.
간략하게 만들어 화면은 아래와 같다.
4.1 게임판의 표현
게임판은 JPanel을 상속하는 Cell을 가로, 세로 size 개수 만큼의 요소로 갖는 이차원 배열로 표현하였다.
LifeGame 클래스는 게임의 전체적인 흐름을 관리하는 메소드들을 갖는다.
public class LifeGame extends JPanel {
...
private void init() {
setLayout(new GridLayout(size,size));
cells = new Cell[size][size];
for (int i=0; i < size; i++)
for (int j=0; j < size; j++) {
cells[i][j] = new Cell();
cells[i][j].setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(cells[i][j]);
}
rule = new GameRule(cells);
}
...
public void transition() {
rule.countAliveNeibor();
rule.applyRule();
}
...
}
4.2 셀의 표현과 셀의 상태
Cell은 live 상태(true이면 살아있고, false 죽어있는 상태)와 살아있는 이웃의 개수에 대한 변수를 갖는다.
live 상태에서 따라서 셀이 그려지거나 혹은 그려지지 않거나 한다.
public class Cell extends JPanel {
private boolean live = false;
private int neighborCount = 0;
private Image img = null;
private int w, h;
...
@Override
protected void paintComponent(Graphics g) {
...
if (live) {
gg.drawImage(img, 0, 0, this);
}
else {
gg.setColor(getBackground());
gg.fillRect(0, 0, getWidth(), getHeight());
}
g.drawImage(image, 0, 0, this);
}
}
4.3 게임규칙의 표현
턴마다 LifeGame 클래스의 transition()메소드가 호출되면, GameRule 클래스의 countAliveNeibor() 메소드가
먼저 호출되고, 이웃하는 셀들에 개수를 모두 카운팅 한뒤, 살아있는 세대를 결정하기 위해 applyRule()메소드가 호출된다.
public class GameRule {
...
public void countAliveNeibor() {
...
}
public void applyRule() {
int neighborCount;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
neighborCount = cells[r][c].getNeighborCount();
if (cells[r][c].getLife()) {
if (neighborCount <= 1 || neighborCount >= 4)
cells[r][c].setLife(false);
}
else {
if (neighborCount == 3)
cells[r][c].setLife(true);
}
}
} //for
}
}
5. 버려진 프로그램은 싫다! 리팩토링~
2009년 08월 04일
예전에 만든 라이프게임 UI가 허접해서 라이프게임 알고리즘은 그대로 드고, 드로잉부분만 수정해서 다시 만들어봤다.
기존에는 메인패널 하나에 좌표를 가지고 Graphics 객체의 드로잉 메소드로 그렸지만, 이번에는
셀하나가 JPanel을 상속하게 했고, 셀이 살아있으면, 이미지를 로딩해서 그리게 했다.
빠른 드로잉을 위해 역시 더블 버퍼링을 사용한다
Timer로는 javax.util.Timer를 사용했다.
TimerTask 쓰레드를 정의해서, 2초마다 상태전이(transition) 후 드로잉하도록 했다.
pauser 기능은 wait() / notify() 를 사용해서, TimerTask 쓰레드를 wait()로 대기상태로 만들고
notifyAll()로 다시 깨우도록 처리했다.
6. 개발하게 하면서 이런걸 알게되었다~
1. AWT의 컴포넌트(Canvas)와 Swing의 컴포넌트(JMenu)를 함께 사용하면, Canvas에 가려 메뉴가 안보인다
중량컨테이너(High Weight Container)인 AWT 와 경량컨테이너(Light Weight Container)인 Swing을 함께
사용해서 그렇다고 한다. 따라서 중량 컨테이너와 중량 컨테이너를 함께 사용하지 말아야 한다.
Swing에서는 Canvas대신 Panel을 사용한다
2. Javax.swing.Timer 객체를 통해 타이머를 구현할 수 있다.
Javax.swing.Timer timer = new Timer(int timeoutMil, ActionListener listener);
3 . 더블버퍼링
Image buffer = Component.createImage(int width, int height); 컴포넌트에 버퍼를 얻고,
Graphics g = buffer.getGraphics(); 그래픽스 객체 g에 드로잉한다
Componet.getGraphics().drawImage(buffer, posintX, positionY, Componet); 버퍼를 컴포넌트에 덮는다
4. 이차원 배열의 사용
Cell을 표현하는 이차원 배열의 생성은 다음과 같다
Cell[][] cell = new Cell[size][size];
for (int i=0; i < size; i++)
for (int j=0; j < size; j++)
cell[i][j] = new Cell();
주의할 점은 Cell 타입의 이차원배열을 생성하고, 그 이차원 배열을 순회하며 실제로 각 배열의 원소에 Cell 객체를
생성하고 할당해야 한다는 것!
7. 만들면서 삽질하게 만든 요인들
첫째로,
타이머 이벤트 발생할 때마다, JPanel 클래스를 상속한 MyPanel에서 public void paint(Graphics g)
메소드를 오버라이드 했는데, 해당좌표에 셀이 도무지 제대로 그려지지 않았다 --^;
갖은 실수 끝에 실수로 알고리즘 메소드가 주석처리 되었었다는 사실을 깨닫고 수정했다. otz...
둘째로,
이전 라이프셀이 지워지지 않는채, 계속 덮입혀져 그려진다. 이것은 더블버퍼링을 이용해서 해결!
생각보다 자바의 드로잉은 속도가 느려서 더블버퍼링을 사용하지 않으면, 화면갱신이 드로잉을 따라가지
못하는 것 같다.
셋째로,
AWT에서의 Canvas 대신, Swing에서는 JPanel을 사용하는데, 드로잉을 하는데 있어서 잘 생각해야 한다.
프로그램이 시작되자 마자 JPanel에 무엇이 그려져야 한다면, 더더욱 그렇다. 게임이 시작하자마자 셀라인을
보이게 하려고 했다. JFrame의 생성자에 그리는 코드를 넣을게 아니라, public void paint(Graphics g) 메소드에서
처리하면 해결할 수 있다. 이때 그리는데 필요한 데이터값이 정확히 대입이 됐는지 잘 살펴야, 삽질을 피할수 있다.
어쨌든 이래저래 해서, 몸풀기 자바 프로그램을 완성했다. 요거 하면서 그나마 잊어버렸던 자바 지식들을 조금씩
기억하게 되어 도움이 되었다.
8. 짜잔~ 최종적으로 만들어진 프로그램
소스 다운로드
다음은 LifeGame, GameRule, Cell 클래스 소스이다.
package lifegame;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class LifeGame extends JPanel {
private Cell[][] cells;
private int size;
private GameRule rule;
public LifeGame(int size) {
this.size = size;
init();
}
// 초기화
private void init() {
setLayout(new GridLayout(size,size));
cells = new Cell[size][size];
for (int i=0; i < size; i++)
for (int j=0; j < size; j++) {
cells[i][j] = new Cell();
cells[i][j].setBorder(BorderFactory.createLineBorder(Color.BLACK));
add(cells[i][j]);
}
rule = new GameRule(cells);
}
public void cellAlive(int r, int c) {
cells[r][c].setLife(true);
}
public void cellDead(int r, int c) {
cells[r][c].setLife(false);
}
public boolean getLifeCell(int r, int c) {
return cells[r][c].getLife();
}
public void clearGame() {
for (Cell[] cs : cells)
for (Cell c : cs)
c.setLife(false);
}
public void transition() {
rule.countAliveNeibor();
rule.applyRule();
}
@Override
protected void paintComponent(Graphics g) {
for (Cell[] cs : cells)
for (Cell c : cs)
c.repaint();
}
}
package lifegame;
public class GameRule {
private Cell[][] cells;
private static final int LEFT = -1, UP = -1;
private static final int RIGHT = 1, DOWN = 1;
private int rows, cols;
public GameRule(Cell[][] cells) {
this.cells = cells;
rows = cells.length;
cols = cells[0].length;
}
// 각 셀의 살아있는 이웃을 센다
public void countAliveNeibor() {
int nNeighbor;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
nNeighbor = 0;
if (r + UP >= 0)
if(cells[r + UP][c].getLife())
nNeighbor++;
if (r + UP >= 0 && c + RIGHT < cols)
if (cells[r + UP][c + RIGHT].getLife())
nNeighbor++;
if (c + RIGHT < cols)
if (cells[r][c + RIGHT].getLife())
nNeighbor++;
if (r + DOWN < rows && c + RIGHT < cols)
if (cells[r + DOWN][c + RIGHT].getLife())
nNeighbor++;
if (r + DOWN < rows)
if (cells[r + DOWN][c].getLife())
nNeighbor++;
if (r + DOWN < rows && c + LEFT >= 0)
if (cells[r + DOWN][c + LEFT].getLife())
nNeighbor++;
if (c + LEFT >= 0)
if (cells[r][c + LEFT].getLife())
nNeighbor++;
if (r + UP >= 0 && c + LEFT >= 0)
if (cells[r + UP][c + LEFT].getLife())
nNeighbor++;
cells[r][c].setNeighborCount(nNeighbor);
}
} // for
}
// 살아있는 셀은 이웃이 1명 이하, 4명 이상이면 죽는다
// 죽어있는 셀은 이웃이 3명이면 살아난다
// 이웃이 2,3명인 살아있는 셀은 계속 산다
public void applyRule() {
int neighborCount;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
neighborCount = cells[r][c].getNeighborCount();
if (cells[r][c].getLife()) {
if (neighborCount <= 1 || neighborCount >= 4)
cells[r][c].setLife(false);
}
else {
if (neighborCount == 3)
cells[r][c].setLife(true);
}
}
} //for
}
}
package lifegame;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.io.BufferedInputStream;
import javax.swing.JPanel;
import javax.imageio.ImageIO;
@SuppressWarnings("serial")
public class Cell extends JPanel {
private boolean live = false;
private int neighborCount = 0;
private Image img = null;
private int w, h;
public Cell() {
try {
img = ImageIO.read(new BufferedInputStream(Res.class.getResourceAsStream("../life.png")));
w = img.getWidth(this);
h = img.getHeight(this);
setPreferredSize(new Dimension(w,h));
} catch (Exception e) {
e.printStackTrace();
}
}
public void setLife(boolean s) {
live = s;
}
public boolean getLife() {
return live;
}
public void setNeighborCount(int n) {
neighborCount = n;
}
public int getNeighborCount() {
return neighborCount;
}
@Override
protected void paintComponent(Graphics g) {
// 더블 버퍼링
Image image = createImage(getWidth(), getHeight());
Graphics gg = image.getGraphics();
if (live) {
gg.drawImage(img, 0, 0, this);
}
else {
gg.setColor(getBackground());
gg.fillRect(0, 0, getWidth(), getHeight());
}
g.drawImage(image, 0, 0, this);
}
}
'언어로그 > Java' 카테고리의 다른 글
| [java] 글 목록 (0) | 2015.09.03 |
|---|---|
| [Java] 자바란? (0) | 2013.02.16 |
| [java] 라이프 게임 (life game) (1) | 2013.02.03 |
| [java] 제13회 한국자바개발자 컨퍼런스 (0) | 2013.01.30 |
| Inner Class(내부 클래스) (0) | 2011.04.12 |
| 컬렉션(Collection) (0) | 2011.04.11 |
설정
트랙백
댓글
글
[연말정산] 월세공제 확정일자에 대한 잘못된 진실
직장인에 13월의 월급이라는 연말정산!
올해는 돈을 뱉어내지? 않기 위해 유난히 사투를 벌였습니다. 표현은 이렇게 했지만
엄밀히 말하면 정당한 혜택을 얻기 위해 그만큼 알아보고 노력했다는 이야기 이지요
그중 가장 골머리를 앓았던 건 주택자금 월세공제를 받기 위한 요건있었습니다.
1. 확정일자 무엇이 진실인가?
* 임대차계약서상에 확정일자가 필요하냐 하지 않느냐?
* 2013년 1월에 확정일자를 받았을 경우, 2012년 연말정산 월세공제에 적용되는가
월세공제를 받을 수 있나? 없나? 에 대한 고민을 한참했지요. 여기서 받을 수 없을 거라고 지례 생각하고
그냥 넘어가는 사람들이 많은데, 전 좀더 자세히 알아봐야겠다는 생각을 했습니다.
2. 공제요건
먼저 공제요건에 대해 알아보면
1. 과세기간 종료일(12/31) 부양가족이 있는 무주택 세대주 혹은 단독 세대주
2. 과세기간(1/1~12/31) 연봉이 5천만원 이하
3. 월세액 외 보증금 등을 지급하는 경우에는 임대차계약서에 확정일자를 받을 것
4. 임대차계약서와 주민등록등본상에 주소지가 같을 것
5. 국민주택 규모(85㎡=25.7평) 이하의 국민주택(오피스텔, 고시원 제외)를 임대한경우
=> 월세액의 40%를 300만원 한도로 공제받을 수 있습니다.
3. 확정일자 이후로 납입된 월세금액에 대해서만 공제가 가능하다?
일반적으로는 확정일자 이후로 납입된 월세금액에 대해서만 공제가 가능하다? 고 알고 있습니다.
인터넷을 검색해 보면 어떤 곳에서는 그렇지 않다는 의견이 있어서 의아해하던 차에
지인을통해 확정일자와 관련하여 국세청에 문의한 내용을 공유받았습니다.
확정일자는 보증금에 대한 공제를 받기 위해 필요한 것이며 월세액에 대한 공제를 받기 위해서는
필요하지 않다는 답을 들었다고 했습니다.
"콜센터 답변자는 알바여서 정확하지 않다" 는 반대의견을 어디선가 듣고 ^^;;
위 내용을 좀더 정확히 확인하기 위해서 관련법규를 찾아보았습니다.
국세청 > 국세법령정보시스템 사이트에 접속하면 아래와 같이 세목별 법규 확인이 가능합니다.
소득세법 제52조 보라색의 밑줄친 부분을 보면
"월세액 외에 보증금 등을 지급한 경우에는 임대차계약서상에 확정일자를 받을 것"
이라는 항목만 있고, 확정일자의 귀속년도에 대한 언급은 없습니다. otz...
위 문구만을 가지고는 해석을 다양하게 할 수 있기 때문에 굉장히 애매한 부분입니다.
그래서...
다시 세무서에 전화로 문의를 해보았습니다. 담당자가 이야기하길...
확정일자에 대한 사항이 이슈가 되서 상급기관에 문의를 넣은 상태이고 거기에 대한 회신이
오지 않았지만 잠정적으로는 확정일자가 2013년도 일지라도 공제를 허용한다는 방향으로 회신이
올거라는 답을 들었습니다.
4. 결론
즉, 결론을 내리자면...
확정일자를 받았다면 그 시기와는 무관하게 귀속년도(2012년)에 납부한 월세액 모두에 대한 공제를 받을 수 있다.
여기까지 제가 알아본 사항들입니다.
혹시나 확정일자 이후에 납부한 금액에 대하서만 적용하셨거나
아에 공제를 받지 못한분들은 위의 내용 숙지하지고 정상적으로 월세공제 받을 수 있기를 바랍니다!!
미흡한 글이지만 조금이라도 도움이 되셨다면 작은 도움의 손길 ^^ 부탁드릴게요!
'기타' 카테고리의 다른 글
| 확 굴려버려 - 볼링(Bowling) (0) | 2014.01.09 |
|---|---|
| [패션] 남자 구두 종류 (1) | 2013.08.05 |
| [연말정산] 월세공제 확정일자에 대한 잘못된 진실 (2) | 2013.01.31 |
| 지갑 (0) | 2011.05.26 |
| 2011년 자바 개발자 컨퍼런스 등록!! (1) | 2011.05.23 |
| Base64 Encoding (0) | 2011.02.18 |
설정
트랙백
댓글
글
[java] 제13회 한국자바개발자 컨퍼런스
올해도 어김없이 코엑스 그랜드볼룸에서 열리는 한국개발자 컨퍼런스 !
2011년 난생 처음 JCO를 방문하고 두근거렸던 설렘이 엊그제 같은데
어느덧 2년이라는 시간이 흘렀네요ㅜㅠ
아이폰 개발을 하면서 자바에는 소홀해졌었는데...이번 JCO를 기점으로 다시한번
자바에 대한 열정을 불태워야겠습니다!!
11회 때 축하공연을 한 달샤벳도 굉장히 인상적이었는데, 12회때는 축하공연이 없어서
살짝 아쉬웠다는... ㅎㅎ
유익하고 개발자들의 탐구욕을 자극시켜줄 한국 자바 개발자 컨퍼런스를 기대해 봅니다.
보너스로 ... 11회때 공연마치고 차량으로 복귀하는데 "이쪽 좀 봐주세요!" 하는 외침에
난감한 표정으로 답하는 세리양 이네요 ㅎ
'언어로그 > Java' 카테고리의 다른 글
| [Java] 자바란? (0) | 2013.02.16 |
|---|---|
| [java] 라이프 게임 (life game) (1) | 2013.02.03 |
| [java] 제13회 한국자바개발자 컨퍼런스 (0) | 2013.01.30 |
| Inner Class(내부 클래스) (0) | 2011.04.12 |
| 컬렉션(Collection) (0) | 2011.04.11 |
| 리플렉션으로 Getter 와 Setter 검사하기 (0) | 2011.04.07 |
설정
트랙백
댓글
글
[iPhone] iOS6 회전 지원하기
iOS6.0 부터 회전을 지원하는 delegate 메소드가 조금 변경되었습니다.
iOS6.0 업데이트 후 회전이 올바로 동작하지 않는 것을 보고 적잖이 당황했었습니다.
관련내용을 찾아보니 의외로 어렵지 않게 6.0에서도 회전에 대응할 수 있었습니다~
그럼 6.0 이상 및 하위버전에서 회전을 올바로 지원하기 위한방법에 대해서 알아봅시다
홈 버튼을 기준으로 왼쪽으로 회전, 오른쪽으로 회전, 거꾸로 뒤집었을 때 등 각 방향을 지칭하게 됩니다!
먼저 기존에는 회전을 지원하기 위해 아래와 같은 스텝을 거쳤습니다.
1. Window에 View 추가
[self.window addSubview:viewController.view]
2. UIViewController 에 지원 회전방향 delegate 구현
// 자동회전 지원하는 방향
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
3. UIViewController 에서 회전하기 전/후 호출되는 delegate 구현
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// 회전하기 전에 호출됩니다.
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
// 회전 후에 호출됩니다.
}
shouldAutorotateToInterfaceOrientation의 UIInterfaceOrentation 파라미터 타입은 다음과 같이
enum 타입 상수로 선언되어 있습니다.
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
};
iOS 6.0 에서는 무엇을 바뀌었을까요?
iOS6.0에서는 1번과 2번 스텝이 변경되었습니다.
1. Window에 rootViewController 추가
self.window.rootViewController = viewController;
기존에는 Window의 View를 추가했지만, iOS6.0 부터는 반드시 Window의 rootViewController로 지정해야 합니다.
2. UIViewController 에 지원 회전방향 delegate 구현
// 자동회전 지원 유무
- (BOOL) shouldAutorotate {
return YES;
}
// 지원하는 회전방향
- (NSUInteger)supportedInterfaceOrientations {
return (1 << UIInterfaceOrientationPortrait) | (1 << UIInterfaceOrientationLandscapeLeft) | (1 << UIInterfaceOrientationLandscapeRight);
}
기존에 2가지 역할을 하던 메소드가 자동회전 유무와 회전하는 방향을 반환하는 메소드로 각각 분리되었습니다.
또 지원하는 회전방향의 경우, NSUInterger 반환값으로 변경되었는데 이 값은 지원하는 회전방향의 비트가 셋팅된
비트 플래그 값입니다.
typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft |
UIInterfaceOrientationMaskLandscapeRight),
};
1을 UIInterfaceOrentation 상수값 만큼 왼쪽으로 시프트해주면, 해당하는 방향의 비트를 셋팅할 수 있지요.
또 한가지 중요한 사항은, 커스텀 UINavigationController와 UITabBarController를 사용한 경우(상속해서)
이 커스텀 클래스의 회전 지원 delegate 메소드에서는 topViewController의 회전지원 delegate 메소드로
위임해야 한다는 것입니다. 아래와 같이 말이죠!
...
- (BOOL) shouldAutorotate {
return [topViewController shouldAutorotate];
}
...
다음은 iOS6.0 이상 및 미만에서 회전을 올바로 처리하기 위한 코드를 정리한 것입니다.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// iOS6.0 이상
NSString *currentVersion = [[UIDevice currentDevice] systemVersion];
NSLog(@"%@", currentVersion);
if ([currentVersion compare:@"6.0" options:NSNumericSearch] != NSOrderedAscending) {
self.window.rootViewController = self.viewController;
} else {
// 하위버전
[self.window addSubview:self.viewController.view];
}
[self.window makeKeyAndVisible];
return YES;
}
#pragma mark - 회전지원 iOS6.0 이상
// 자동회전 지원유무
- (BOOL) shouldAutorotate {
return YES;
}
// 회전방향 지원유무
- (NSUInteger)supportedInterfaceOrientations {
return (1 << UIInterfaceOrientationPortrait) |
(1 << UIInterfaceOrientationPortraitUpsideDown) |
(1 << UIInterfaceOrientationLandscapeLeft) |
(1 << UIInterfaceOrientationLandscapeRight);
}
#pragma mark - 회전지원 iOS6.0 미만
// 회전방향 지원유무
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown
);
}
#pragma mark - 회전 델리게이트
// 공통
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
// 회전하기 전에 호출됩니다.
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
// 회전 후에 호출됩니다.
}
소스코드 다운로드
'아이폰' 카테고리의 다른 글
| [iPhone] 아이폰앱 디자인 정석(TapWorthy) (1) | 2013.07.07 |
|---|---|
| [iPhone] 아이폰 앱 디자인 실수: 문맥에 대한 간과 (0) | 2013.02.16 |
| [iPhone] iOS6 회전 지원하기 (0) | 2013.01.25 |
| [iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation) (0) | 2013.01.23 |
| [Mask 레이어 활용] 이미지 일부만 보여주기 (2) | 2013.01.22 |
| [번역] 사용자 인터페이스 설계의 원리 (0) | 2012.06.24 |
설정
트랙백
댓글
글
[iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation)
아이폰에서는 애니메이션을 쉽게 구현할 수 있는 수단들을 제공한다
UIView의 위치 이동, 크기변경, 회전, 페이드인/아웃 등과 간단한 애니메이션은 UIView 애니메이션을
사용해서 구현할 수 있다. UIView 클래스에 애니메이션을 위한 메서드들이 존재하며,
iOS 4.0 부터는 블럭방식의 애니메이션 메소드를 제공해서 코드단에서 편리하게 구현할 수 있다.
CoreAnimation은 CALayer 단에 적용되며, UIView 애니메이션 효과를 구현할 수 있을 뿐만 아니라
좀더 복잡하고 세밀한 애니메이션 효과를 줄 수 있다. 하지만 UIView 애니메이션보다는 코딩량이 좀더 많고 번거롭다
간략히 UIView 애니메이션과 코어 애니메이션을 사용하는 방법에 대해서 알아보자
다음은 UIView를 사용하여 구현한 푸시 애니메이션(네비게이션 푸시가 아니라, 버튼이 움푹 눌리는 듯한 효과)과
CoreAnimation을 사용하여 구현한 플립 애니메이션(y축을 기준으로 뒤집어 지는 듯한 효과)이다
1. UIView 애니메이션을 사용한 푸시효과
구현방식은 터치가 발생하면 x, y축 스케일(scale)을 0.3초 동안 0.9로 축소하고
다시 0.3 초 동안 1.0으로 되돌아 오는 효과를 적용했다. UI 요소가 눌리는 듯한 효과를 줄 수 있으며, 인터렉티브한
느낌을 줄 수 있어서 자주 사용하는 효과중에 하나이다.
[UIView animateWithDuration:0.3 animations:^(void) {
self.transform = CGAffineTransformMakeScale(0.9, 0.9);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^(void) {
self.transform = CGAffineTransformIdentity;
}];
}];
animations 블럭에서 속성을 변경하는 코드가 비동기 방식으로 (메인쓰레드에서) 애니메이션되는 방식이다.
위 코드는 가로, 세로 Scale을 각각 0.9로 변환하는 애니메이션을 0.3초 동안 수행하고,
애니메이션이 완료(completion) 하면 원래 스케일로 복원하는 애니메이션을 다시 0.3초 동안 수행한다.
CGAffineTransform 즉 아핀변환은 벡터에서 등장하는 용어로 변환을 행렬을 사용해서 나타낸 것이라는 정도만 알아두자.
2. CoreAnimation을 사용한 플립효과
CoreAnimation 은 UIView가 아닌 CALayer에 적용되며, 애니메이션할 CALayer의 property를 지정하고
애니메이션 상태를 좀더 세밀하게 제어할 수 있는 속성을 제공한다.
플립 애니메이션을 구현하는 동안 주의할 점은 y축을 기준으로 회전을 하면 이미지가 좌우가 뒤집어 지는
현상이 발생한다는 것이다. 레이어를 회전시켰기 때문에 뒤집어 지는 것이다.
이문제는 먼저 90도를 회전 시키고, -90로 뒤집은 상태에서 다시 나머지 회전을 수행하는 방식을
사용하면 해결 할 수 있다.
- (void)flip {
// 90도 회전
CABasicAnimation *rotateY = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotateY.beginTime = 0.0;
rotateY.toValue = [NSNumber numberWithFloat:radians(90)];
rotateY.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
rotateY.autoreverses = NO;
rotateY.repeatCount = 1;
rotateY.removedOnCompletion = NO;
rotateY.duration = 0.4;
rotateY.fillMode = kCAFillModeForwards;
// -90도로 뒤집은 상태에서 나머지 회전
CABasicAnimation *rotateY2 = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
rotateY2.beginTime = 0.4;
rotateY2.fromValue = [NSNumber numberWithFloat:radians(-90)];
rotateY2.toValue = [NSNumber numberWithFloat:radians(0)];
rotateY2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
rotateY2.autoreverses = NO;
rotateY2.repeatCount = 1;
rotateY2.removedOnCompletion = NO;
rotateY2.duration = 0.4;
rotateY2.fillMode = kCAFillModeForwards;
CAAnimationGroup *group = [CAAnimationGroup animation];
group.animations = [NSArray arrayWithObjects:rotateY, rotateY2, nil];
group.duration = 0.8;
group.autoreverses = NO;
group.repeatCount = 1;
group.removedOnCompletion = NO;
group.fillMode = kCAFillModeForwards;
group.delegate = self;
[self.layer addAnimation:group forKey:nil];
}
CAAnimationGroup은 동시에 여러 애니메이션을 복합적으로 적용할 때 사용하며, CABasicAnimation의
beginTime 속성을 사용해서 애니메이션 시작 시점을 조정할 수 있다. 총 0.8초 동안의 시간동안 처음 0.4초 동안
90도 회전하고, 0.4초 후 -90도 뒤집어진 상태에서 0도로 0.4초 동안 회전시켜 마치 카드가 자연스럽게
뒤집어 지는 듯한 플립효과를 구현하였다
거의 대부분의 애니메이션은 UIView 애니메이션으로 구현할 수 있지만
UI/UX에 특화된 좀더 재미나고 멋진 효과를 가진 앱을 개발하고 싶다면 코어 애니메이션 사용을 주저하지 말자!
소스코드
'아이폰' 카테고리의 다른 글
| [iPhone] 아이폰 앱 디자인 실수: 문맥에 대한 간과 (0) | 2013.02.16 |
|---|---|
| [iPhone] iOS6 회전 지원하기 (0) | 2013.01.25 |
| [iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation) (0) | 2013.01.23 |
| [Mask 레이어 활용] 이미지 일부만 보여주기 (2) | 2013.01.22 |
| [번역] 사용자 인터페이스 설계의 원리 (0) | 2012.06.24 |
| 수동으로 UI 컨트롤에 이벤트 전달 (0) | 2011.11.13 |
설정
트랙백
댓글
글
[Mask 레이어 활용] 이미지 일부만 보여주기
마스크 레이어를 활용하여 이미지의 일부부만 노출시키는 예제를 작성해 보았다.
핵심은 마스크 레이어를 사용하면 터치 이벤트를 수신할 수 없기 때문에
상위에 터치 이벤트를 수신하여 마스크 레이어 단에 이벤트를 전달해주는 투명뷰를
덮는 것 !
간단한 예제인데 fx의 설리와 크리스탈을 배경 이미지로 사용하니 재미있는 예제가 됐다.
(나도 이제 나이 들었나봐 ㅠㅜ 바라만 봐도 행복하구나 ㅋ)
아래는 예제를 실행한 사진이다.
자! 이제 어떻게 예제를 구현했는지 알아보자!
구현방식을 간단히 그림으로 나타내 보면 아래와 같다.
SBTouchView 를 완전히 투명하게 해서는 안된다. 완전히 투명할 경우에는 터치 이벤트를 수신하지 못하기 때문에
투명도를 0.1로 하는 꼼수를 발휘했다. SBTouchView는 터치 이벤트를 단순히 nextResponder 에게 전달하고,
SBMaskedView가 그 이벤트를 받게되고, 마스크 영역을 이동시킨다.
구현 절차는 아래와 같다.
1. 마스크 레이어 설정
maskLayer = [[CAShapeLayer layer] retain]; maskLayer.backgroundColor = [UIColor whiteColor].CGColor; pathRect = CGRectMake(1, 1, 118, 118); UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect]; maskLayer.path = path.CGPath; maskLayer.fillColor = [UIColor blackColor].CGColor;
핵심은 레이어의 배경색을 흰색으로 처리하고, 마스킹 시킬 영역(Path)만 검정색으로 칠하는 것입니다.
2. 배경레이어에 마스크 설정
// 배경이미지 (크리스탈, 설리 이미지) CALayer *bgLayer = [CALayer layer]; UIImage *bgImage = [UIImage imageNamed:@"christal.jpeg"]; bgLayer.frame = [self frameForImage:bgImage]; bgLayer.contents = (id)bgImage.CGImage; bgLayer.mask = maskLayer; [self.layer addSublayer:bgLayer];
배경(이미지) 레이어의 mask 속성에 마스크 레이어를 설정합니다.
3. 터치뷰를 설정
// 터치 이벤트를 잡을 투명뷰 SBTouchView *touchView = [[[SBTouchView alloc] initWithFrame:self.bounds] autorelease]; [self addSubview:touchView];
// SBTouchView
@implementation SBTouchView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.1];
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[[self nextResponder] touchesCancelled:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[[self nextResponder] touchesMoved:touches withEvent:event];
}
4. 터치 이벤트를 수신하여 마스크 레이어 이동 시키기
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
if (CGRectContainsPoint(pathRect, point)) {
touchBegan = YES;
touchPoint = point;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (touchBegan) {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGPoint destPoint = CGPointMake(pathRect.origin.x + (point.x - touchPoint.x),
pathRect.origin.y + (point.y - touchPoint.y));
pathRect.origin.x = destPoint.x;
pathRect.origin.y = destPoint.y;
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:pathRect];
maskLayer.path = path.CGPath;
touchPoint = point;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
touchBegan = NO;
}
터치 영역이 마스크 레이어의 영역일 경우, 마스크 레이어의 위치를 이동시킵니다.
소스 다운받기
이상 마스크 레이어 사용법을 알아보면서 간단한 예제도 만들어 보았다.
아이폰을 하면서 항상 느끼는 거지만...UI 프로그래밍이 생각보다 재밌다는 것!
'아이폰' 카테고리의 다른 글
| [iPhone] iOS6 회전 지원하기 (0) | 2013.01.25 |
|---|---|
| [iPhone] UIView 애니메이션과 코어 애니메이션(CoreAnimation) (0) | 2013.01.23 |
| [Mask 레이어 활용] 이미지 일부만 보여주기 (2) | 2013.01.22 |
| [번역] 사용자 인터페이스 설계의 원리 (0) | 2012.06.24 |
| 수동으로 UI 컨트롤에 이벤트 전달 (0) | 2011.11.13 |
| NSNotification (1) | 2011.04.29 |
설정
트랙백
댓글
글
2011년 스마트폰 사용자 통계

Infographic by- GO-Gulf.com Web Design Company
'앱개발로그' 카테고리의 다른 글
| [Android] 안드로이드앱 개발 도전기 (2) | 2013.03.31 |
|---|---|
| 2011년 스마트폰 사용자 통계 (0) | 2012.11.12 |
| BlackBerry Architecture (0) | 2011.04.26 |
JDK_000_LifeGame.zip