iPhone에서 그래프 그리기 (Objective-C)

올해부터 아카이브소속 개발자가 된  “공업철학 프로그래머” 입니다. 여기 개발자 블로그에 첫글을 쓰게 되는군요. 가끔씩 제 블로그에 기술적인 내용을 올리긴 했지만 이제부터는 개발에 관련된 것은 여기에 올릴 생각입니다. 혹시나 저의 글이 궁금하신 분은 아래 블로그를 봐주세요. (내세울 만한  실력이 아니라서 부끄럽네요.) 그럼 잘못된내용이나 지적 또는 질문등 환영합니다.
http://dolfalf.tistory.com
http://dolfalf.wordpress.com

개요

그래프 또는 챠트와 같은 복잡한 구조의 뷰는 iOS 표준컨트롤로는 대응할 수 없다. 설사 UIView를 그럴싸하게 배치함으로서 효과를 내는 방법등으로 구현할 수도 있겠지만 UIView자체로 여러기능이 추상화된 클래스라 뷰의 숫자가 많아지면 표시만으로 성능에 문제가 발생할 수 있다.
그러므로 UIView를 상속받아 하나의 뷰에서 직접 그리는 방법으로 구현하는 것이 일반적이다.

최근 너무나도 훌륭한 오픈소스들이 많이 있으므로 그것을 활용하는 것도 좋지만, 만들어야 하는 그래프, 디자인, 그리고 특수한 기능등이 있다면 어떻게 만들어져 있는지 파악하는것이 이해와 응용에 도움이 되리라 생각된다.

기술적인 세부적인 내용은 Stackoverflow의 도움을 받길 바라며 대략적으로 개발에 필요한 키워드를 설명하는 수준에서 글을 쓰려한다.

사전에 생각해 두어야 할 것들.

먼저, 일반적인 선 그래프를 한번 생각해 보자.

우선  X축과 Y축이 있고 그래프안에는 구분선과 실제 그래프가 그려질 것이다. 그리고 X, Y의 단위를 표시하는 라벨이 필요할 것이다.

첫번째로 고려해야 될것은 이 그래프가 그려지는 뷰의 크기(전체크기)를 알수 없기 때문에 고정크기가 아닌 상대크기로 계산해야한다는 것이다.

두번째로는 그리는 타이밍의 문제인데 데이타의 변경에 의해 바뀌는 부분과 그렇지 않은 부분을 나누어서 생각해야 한다. 그렇지 않으면 화면갱신시 꽤 어색한 움직임을 보일것이다. 여기서는 X, Y축 그리고 그리드선은 변하지 않는 부분, 그래프(빨간색)은 변경되는 부분이다.

세번째로는 어느정도 디자인변경이 가능하도록 고려해야 한다는것이다. 예를들어 X축이 오른쪽으로 해달라고는 요구가 있을 수도 있고 또는 Y축라벨밑에 또는 윗부분에 그래프 제목을 넣고 싶다던가 하는것들이다.

그럼 위의 세가지 사항을 고려하여 설계한다면 다음과 같은 영역으로 나뉠수 있다.

전체 뷰 크기를 view_frame라고 하면, 실제로 그래프가 그려지는 영역은 canvas_frame이 될것이고 xxx_margin에는 요구사항에 대응할 수 있는 여백을 준비해 두었다. 예시와같이 Y축을 왼쪽에 둔다면 right_margin을 0으로 설정함으로써 유연하게 대응할 수 있게된다.

이로써 최상위뷰의 크기 (view_frame)를 기준으로 상대적으로 canvas_frame의 크기와 각 공백영역의 크기와 위치를 구할 수 있다.

예를들어 view_frame의 크기는 self.bounds 가 될것이고 canvas_frame의 크기는,

  • x = view_frame.origin.x + left_margin
  • y = view_frame.origin.y + top_margin)
  • width = view_frame.size.width – (left_margin + right_margin)
  • height =  view_frame.size.height – (top_margin + bottom_margin)

와 같이 계산될 수 있을것이다.

canvas의 위치가 계산됬다면 그래프를 그리기 위한 좌표 계산을 하면 된다. 중학교때 배운 방정식을 떠올려보자.

그래프에 표시될 범위가 0에서 500 이라고 하고 그것을 좌표로 변환하는 식을 계산한다면,

표시데이터값 : 최대표시데이터값 = 표시영역좌표 : 표시영역최대크기

즉, 표시영역좌표 = (표시데이터값 * 표시영역최대크기) / 최대표시데이터값

이된다.

만약 표시영역좌표를 Y라고 하고 데이타가 300, canvas크기를 400 이라고 한다면

Y = (300 * 400) / 500

이라는 식이 만들어지고 실제 뷰에 찍어야될 좌표를 구할 수 있게된다.  그럼 실제 데이타를 좌표로 변환해주는 메소드를 준비해 두자.

 

drawRect

위에서 좌표까지 계산되었으므로  이제 그리기만 하면된다. 그리는건 drawRect라는 UIView의 콜백메소드에서 그려준다. 이 메소드는 뷰가 다시그려줄 필요가 있을때 후처리로 콜백되는 메소드이다.

(첨언하자면, UIView에서 직접 상속받았다면 오버라이드 하더라도 [super drawRect:rect]를 호출할 필요가 없다. UIView클래서에서 후처리를 위한 커스텀 콜백이기 때문이다.)

 

만약 이 콜백메소드를 인위적으로 호출하고 싶을땐 [해당뷰.setNeedDisplay]를 호출하면 된다. 이메소드는 시도때도 없이 불릴수 있으므로 이 그래프에선 배경을 그리는데만 사용할 것이다.

물론 직접그래프까지 그려줘도 되지만 뒤에 설명할 애니메이션 효과라던지 데이터변경에 자연스럽게 갱신되는 것을 생각한다면 여기선 배경만 그려주자.

자세한 내용은 google의 도움을 받자.

UIBeizerPath사용

이 클래스를 이용하여 실제 선그래프를 처리할 것이다. 이 처리는 데이터가 변경되는 타이밍에 그려주면 된다.

이 클래스를 이용하면 직선이나 곡선 도형까지 자유자재로 그릴 수 있다. 자세한 내용은 레퍼런스는 아래 링크의 레퍼런스를 참조하면 된다.

https://developer.apple.com/reference/uikit/uibezierpath

 

애니메이션

위에 클래스(UIBeizerPath)를 이용해서 레이어에 그렸다면 Path를 이용하여 간단하게 애니메이션을 적용할 수 있다. 제대로만 만들었다면 처음 뷰가 열리면서 배경이 그려지고 데이터가 취득되는 타이밍에 그래프가 그려지는 애니메이션 효과를 별 어려움없이 구현할 수 있을것이다. 이 내용또한 검색하면 여기저기 예제가 많이 나오므로 간단한 예제소스를 소개한다. (애니메이션종류는 여러 타입이 있으므로 CABasicAnimation클래스의 레퍼런스를 참조하면 된다.)

마지막으로,

써놓고 보니 설명이 좀 허술하지만 처음 그래프를 그려야될 개발자에게 도움이 되도록 썻다. 부디 이글이 도움이 되었으면 하는 바램이다.

この投稿へのコメント

コメントはありません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

トラックバック・ピンバック

トラックバックはありません。

トラックバック URL