조금 더 재밌는 앱 만들기
이제 본격적으로 '태양계 행성 앱'을 만들어 보겠습니다.
1. 행성 이미지가 있고 이미지를 누르면 행성에 대한 정보를 알 수 있다.
2. 옆으로 넘기면서 행성을 탐색할 수 있다.
위 두 개의 기능을 먼저 구현해보겠습니다.
먼저 행성 이미지가 필요하겠네요. 이미지는 모두 위키피디아에서 사용하였고 하단에서 다운로드할 수 있습니다.
먼저 다운로드한 이미지를 프로젝트의 Assets.xcassets에 추가합니다. 일반적으로 xcassets은 아이콘 이미지, 앱 아이콘을 관리하지만 편의상 행성 이미지도 해당 공간에 추가하겠습니다.
1x, 2x, 3x는 아이폰이 레티나 디스플레이를 지원하는지에 따라 이미지를 다르게 제공해줘야 하는 것인데요,
고해상도 디스플레이에는 더 많은 픽셀을 요구하기 때문에 2배 이미지, 3배 이미지를 제공해야 함을 의미합니다. 우선은 고려하지 않아도 좋으며, 1배 이미지로 지정하겠습니다.
이제 기획에 맞게 기능을 구현해보겠습니다.
1. 행성 이미지가 있고 이미지를 누르면 행성에 대한 정보를 알 수 있다.
먼저 보이는 Scene에 UIImageView를 추가합니다.
UIImageView는 View를 상속받은 객체이며 이미지를 보여주기 위해 사용되는 뷰 객체입니다.
즉, View의 기능은 사용할 수 있으면서 추가로 이미지도 보여줄 수 있다고 생각하면 되겠습니다.
우선 배경 전체를 행성 이미지로 채워보겠습니다. 여기서 모든 디바이스에 대응하기 위해선 AutoLayout을 알아야 하는데요, AutoLayout은 첫 앱을 만들며 잠깐 공부했습니다.
AutoLayout은 뷰 간 내부 혹은 외부로 비교하는 상대적인 간격 개념입니다. 또한, AutoLayout이 적용되기 위해선 x값, y값, width값, height값을 모두 정해줘야 합니다.
여기서 가장 최상위 View (화면 전체를 차지하는 View)와 비교해보겠습니다. UIImageView를 화면 전체로 덮기 위해선 최상위 View와의 간격이 어떻게 돼야 할까요?
간격의 차이가 없어야 뷰의 크기가 동일할 것으로 보입니다.
AutoLayout을 적용하는 방법 중 하나인 우하단 3번째 버튼을 클릭합니다.
이 방법은 현재 AutoLayout을 지정해줄 View와 가까이 있는 View 간의 간격을 정해줄 수 있습니다.
여기선 가장 가까이 있는 View가 무엇일까요?
최상위 View라고 생각할 수 있지만 아닙니다.
바로 Safe Area입니다.
Safe Area는 아이폰 X부터 존재하는 notch design을 대응하기 위해 만든 영역입니다.
notch 영역엔 상단에 상태바, 하단에 홈바가 들어가기 때문에 이 영역을 침해하지 않는 영역 정도로 생각하면 좋습니다.
그럼 Safe Area와 상단, 하단, 좌우 간격이 0을 정해보겠습니다. 이렇게 0으로 모두 정하면 x, y, width, height을 모두 정해준 것과 같습니다.
x는 왼쪽 간격, y는 위 간격, width는 오른쪽 간격 그리고 height은 아래 간격으로 정해줬기 때문입니다. (아래 간격이 0이란 것은 SafeArea 영역과 height가 동일하단 것과 같습니다.)
모두 0으로 지정해준 후 Add 4 Constraints 버튼을 누르면 아래와 같이 ImageView가 가득 찬 것을 확인할 수 있습니다.
그리고 오른쪽 영역에서 5번째 버튼 (Attribute Inspector)를 클릭하면 이미지를 지정할 수 있습니다.
태양계 첫 번째 행성인 수성(Mercury)으로 설정해보겠습니다.
UIImageView는 Safe Area 영역과 동일하게 맞췄지만 이미지는 그 크기와 다르게 나타나는 것을 확인할 수 있습니다.
이는 UIImageView가 이미지를 보여주는 설정 값에 따라 달라지는데요, 현재는 이미지의 비율은 지키면서 이미지 뷰의 너비, 높이 중 작은 값에 맞추면서 최대한 크게로 설정했기 때문에 이렇게 노출되는 것입니다.
그렇다면 이 이미지를 가득 채우려면 어떻게 해야 할까요?
비율을 깨고 채우는 방법과 비율을 지키며 채우는 방법 2가지가 있는데요, 일반적으로 비율을 지키며 채우는 방법을 사용합니다.
해당 설정은 이미지 설정과 마찬가지로 Attribute Inspector에서 설정할 수 있습니다.
아래에 Content Mode가 보이시나요?
현재는 Aspect Fit으로 되어있는데요, 이 값이 이미지 뷰의 너비, 높이 중 작은 값에 맞추면서 최대한 크게 설정하는 모드입니다.
AspectFill은 비율에 맞추며 이미지 뷰를 가득 채우는 모드이며, Scale To Fill은 비율을 깨고 이미지 뷰의 너비와 높이에 동일하게 이미지를 설정하는 것입니다.
이 앱에선 기본 모드인 Aspect Fit 모드로 설정하겠습니다. 그렇다면 조금 아쉬운 게 있습니다. 이래 봬도 태양계 행성 앱인데 배경색이 검은색이면 좋지 않을까요? 최상위 뷰를 클릭해서 배경을 검은색으로 변경해보겠습니다.
이제 앱을 실행해보겠습니다.
아.. 다 좋은데 한 가지가 아쉽습니다.
바로 시계와 배터리 잔량 등이 보이지 않는 것입니다. 아무 설정도 하지 않는 다면 상태바에 표시되는 색상은 검은색이기 때문에 배경색과 동일해서 보이지 않습니다.
물론, 게임이나 영상을 다루는 앱에서는 콘텐츠 자체에 집중시키기 위해 상태바 영역을 보이지 않게 설정하기도 합니다.
하지만 태양계 행성 앱에선 시간과 배터리 잔량 등을 보여주고 싶습니다.
어떻게 하면 해당 영역을 검은색에서 흰색으로 변경할 수 있을까요?
방법은 간단합니다.
SolarSystemViewController로 이동하여 overrideUserInterfaceStyle 값을 dark로 변경하면 상태바에 노출되는 UI의 색상이 흰색으로 변경됩니다.
이제 앱을 다시 실행하면 시간과 네트워크, 배터리 잔량을 확인할 수 있습니다.
행성 이미지가 있고 이미지를 누르면 행성에 대한 정보를 알 수 있다.
이제 이미지를 누르면 행성에 대한 정보를 보여주면 될 것 같습니다.
'눌렀을 때 어떤 동작을 하라'고 설정했던 뷰 객체가 있었습니다. 바로 버튼입니다.
사실 버튼이 클릭이 가능한 이유는 UIControl이라는 클래스를 상속받고 있어서..
이미지 뷰는 터치했을 때 일어나는 동작을 따로 설정할 수 없습니다. 그러므로 다른 방법으로 접근해야겠죠.
그 방법으로는 이미지 뷰에 탭 인식기를 달아주면 가능합니다.
Main.storyboard로 돌아가서 이미지 뷰를 추가했을 때처럼 UITapGestureRecognizer를 검색합니다.
검색 결과로 나온 Tap Gesture Recognizer를 드래그하여 이미지 뷰에 놓아줍니다.
이렇게 뷰 계층 구조에 Tap Gesture Recognizer가 추가되어 있는 것을 확인할 수 있습니다.
이렇게 추가된 탭 인식기는 ViewController에 연결하여 동작을 추가할 수 있습니다.
ViewController에 연결할 수 있는 방법은 여러 가지가 있지만 이번엔 화면 분할을 통해 연결해보겠습니다.
command+shift+option+enter를 사용해 화면 분할이 가능합니다.
화면 분할 후 control키를 누른 상태로 Tap Gesture Recoginzer에서 SolarSystemViewController로 드래그합니다. (중괄호 위치에 주의해주세요.)
Name은 함수 이름을,
Type은 UITapGestureRecognizer를 선택하고 Connect 버튼을 클릭합니다.
여기선, planetImageDidTapped를 함수 이름으로 설정하겠습니다.
이제 이미지 뷰를 클릭했을 때 특정 동작을 정할 수 있을까요? 아직 하나가 더 남았습니다.
이미지 뷰에 탭 인식기를 추가했어도 이미지 뷰 자체가 사용자 인터렉션을 허용해야 합니다.
Main.storyboard에서 ImageView를 클릭하고 Attribute Inspector(오른쪽 영역 5번째 버튼)을 보겠습니다.
Interaction에 User Interaction Enabled 옵션이 꺼져 있는 것을 확인할 수 있습니다. 이 옵션이 켜져 있어야 사용자의 인터렉션을 받을 수 있는 View 객체가 되는 것입니다. 체크 박스를 클릭한 후 앱을 실행해보겠습니다.
행성 이미지가 있고 이미지를 누르면 행성에 대한 정보를 알 수 있다.
이제 행성에 대한 정보만 알려주면 되겠습니다. 이미지 뷰 위에 정보를 나타내는 뷰를 추가하겠습니다.
다시 Main.storyboard로 이동하겠습니다.
이미지 뷰 위에 비어있는 뷰를 추가합니다. 비어있는 뷰 역시 이미지 뷰와 동일한 크기를 갖도록 하겠습니다.
이미지 뷰와 추가한 뷰를 Command키를 통해 선택한 후 하단에 있는 버튼 중 이번엔 두 번째 버튼을 클릭합니다. 이 버튼은 선택된 두 개의 뷰에 제약을 줄 수 있습니다. 마찬가지로 모두 0으로 해야 두 개의 뷰의 크기가 동일합니다.
행성에 대한 정보를 보여줄 때 행성의 이미지도 흐릿하게 보여주고 싶다면 어떻게 해야 할까요?
정보를 보여주는 뷰 Background Color의 alpha 값을 조절하면 가능할 것 같습니다. 정보를 보여주는 뷰를 클릭한 후 뷰의 Background 색상을 변경하겠습니다.
이제 거의 끝났습니다. 행성 정보만 보여주면 되겠습니다. 행성 정보는 행성 이름과 위키피디아 설명 정도면 좋을 것 같습니다.
iOS에서 텍스트를 담당하고 있는 뷰 객체는 UILabel입니다. 행성 이름을 나타낼 Label과 행성 정보를 나타낼 Label 총 2개를 추가하겠습니다. 이미지 뷰 추가했던 방법과 같습니다.
Label 2개를 추가했는데 잘 보이지 않습니다. Attribute Inspector에서 Text Color를 흰색으로 변경하겠습니다.
글씨 크기도 조금 아쉽습니다. 행성 이름을 나타내는 Label은 Bold체로 40pt, 행성 정보를 나타내는 Label은 Regular체로 20pt로 설정하겠습니다.
이제 Label의 위치를 설정해줘야겠죠. 모든 디바이스에 대응하기 위해선 Label 역시 AutoLayout을 설정해야 합니다. Label은 Width와 Height이 글씨 크기에 맞춰 고윳값이 설정되기 때문에 우선은 X값과 Y값만 정해주면 됩니다.
행성 이름을 나타내는 Label은 X값은 SafeArea를 기준으로 왼쪽으로부터 20pt, Y값은 위쪽으로부터 40pt만큼 떨어트리겠습니다.
제약을 주는 방법은 동일합니다. Safe Area와 Label을 선택한 후 하단 두 번째 버튼으로 Leading 20, Top 40을 추가해주면 됩니다.
이제 정보를 나타내는 Label만 위치를 제공하면 될 것 같습니다. 마찬가지로 왼쪽으로 20pt 여백을 두고 이름을 나타내는 Label과 20pt만큼 간격을 두겠습니다. 왼쪽 여백은 위에 방법과 동일하게 하면 되지만, 위 여백을 주는 것은 조금 다르게 해줘야 합니다.
위 방법은 같은 Edge를 기준으로 제약을 주기 때문입니다.
만약, 정보를 보여주는 Label에서 Top 값을 20으로 준다면 이름을 제공하는 Label의 Top 기준으로 20pt만큼 떨어트리기 때문에 Label이 겹쳐 보일 것입니다.
이런 경우 이름을 제공하는 Label의 bottom을 기준으로 20pt만큼 떨어트려야 하기 때문에, Vertical Spacing을 설정합니다.
Vertical Spacing을 설정하기 위해선 정보를 보여 줄 Label을 클릭하고 control키를 누른 상태로 이름을 나타내는 Label로 드래그&드롭합니다.
이렇게 Vertical Spacing이 눈에 띄는데요, 클릭합니다.
클릭하면 각 Label 사이에 파란색 선이 생깁니다.
파란색 선이 생겼단 것은 제약이 설정됐단 의미인데요, 지금은 조금 멀리 떨어져 있기 때문에 이 값을 바꿔줘야 합니다.
제약을 바꿔주는 방법은 간단합니다.
파란색 선을 클릭한 후 오른쪽 영역에서 수정합니다.
지금은 53으로 설정되어 있는데 이 값을 우리가 원하는 값인 20으로 변경합니다. 이제 위치 선정이 완료됐기에 Label 대신 행성 이름과 정보를 추가해보겠습니다. Label을 선택한 후 Attribute Inspector에서 Text 값을 변경할 수 있습니다.
정보를 나타내는 Label이 우리가 원하는 방향이 아닌 것 같습니다. 이렇게 표시되는 이유는 Label은 기본적으로 한 줄로 표시되기 때문인데요, Text를 설정하는 영역 아래를 보면 Lines를 설정할 수 있습니다.
여기서 텍스트 양에 맞춰서 Label의 줄 수를 결정하고 싶다면 0으로 설정하면 됩니다.
0으로 설정하셨나요? 안타깝게도 아무 반응도 없을 것입니다. 이유는 제약 조건이 부족하기 때문입니다.
Label의 왼쪽과 위쪽 영역만 줘도 문제가 되지 않지만 이런 경우에는 오른쪽 제약도 필요합니다. Label에서 Line이 변경되긴 위해선 Label이 갖고 있는 Width보다 글씨가 많은 경우인데요, 오른쪽 제약을 주지 않는다면 Width가 제한되지 않기 때문입니다. 왼쪽과 마찬가지로 20pt를 설정하겠습니다.
오른쪽은 Leading과 다르게 -20pt로 설정해야 합니다. Second Item을 기준으로 First Item이 오른쪽 방향으로 차이 난다면 +, 왼쪽 방향으로 차이 난다면 -값으로 설정해야 하기 때문입니다.
여기선 Safe Area를 기준으로 왼쪽 방향이기 때문에 - 값을 설정해주는 것입니다.
만약 First Item과 Second Item을 변경한다면 어떻게 될까요?
여기서 Second Item은 정보를 나타내는 Label입니다. Label을 기준으로 Safe Area가 오른쪽 방향을 차이 나기 때문에 +20이 되는 것입니다.
따라서, 어떤 Item을 기준으로 둘지 먼저 생각한 후 제약 조건을 정하는 것이 필요합니다.
자, 이제 앱을 실행해보겠습니다.
정보가 잘 보이는 것을 확인할 수 있습니다. 이제 정보가 나타나는 뷰를 행성 이미지를 클릭했을 때만 보이도록 수정이 필요합니다.
먼저 앱이 실행됐을 땐 정보를 나타내는 뷰가 보이지 않아야 합니다.
보이지 않는 방법은 간단합니다. hidden 처리를 하거나 View의 alpha 값을 조절하면 가능합니다.
여기선 alpha 값을 0으로 조절해보겠습니다.
이제 정보 뷰가 투명해졌습니다. 이제 이미지가 터치되면 이 정보 뷰의 alpha 값을 1로 조절하면 됩니다.
액션을 연결했던 것과 동일하게 화면 분할 후 정보를 나타내는 뷰를 ViewController로 드래그합니다.
여기서 주의해야 할 점은 이번 Connection은 Action이 아닌 Outlet이라는 점입니다. 이름은 informationView로 설정하고 Connect 버튼을 클릭합니다.
Interface Builder(Main.storyboard)로 부터 연결된 informationView를 확인할 수 있습니다. 이제 우리는 ViewController에서 informationView 값을 자유롭게 사용할 수 있습니다.
planetImageDidTapped 함수를 수정하겠습니다. 이미지가 터치되면 informationView의 alpha 값을 1로 변경하면 된다고 설명했습니다.
이제 행성 이미지를 클릭하면 informationView가 보이는 것을 확인할 수 있습니다.
행성 이미지가 있고 이미지를 누르면 행성에 대한 정보를 알 수 있다.
첫 번째 기능은 구현된 것 같습니다.
만약, 화면을 한번 더 터치하면 informationView가 사라지게 하고 싶다면 어떻게 해야 할까요?
다음 글에서 계속 만들어보겠습니다.
고맙습니다.
출처
https://ko.wikipedia.org/wiki/%EC%88%98%EC%84%B1