iOS 삽질 : 인터페이스 빌더에서 설정한 색상이 제대로 표시 안됨

인터페이스빌더(interface builder) 혹은 스토리보드(storyboard)에서 디자이너간 준 색상 코드를 입력했는데 이상하게 코드로 입력한 색상과 다르게 보이는 현상이 있었습니다.

코드로 입력한건 잘되는데 인터페이스 빌더에서 직접 지정한 색상의 경우에만 원하는 색상이 표시되지 않았죠. 그래서 찾다 보니 Color Space 의 문제임을 알게 되었습니다.

이거는 맥에서 애플이 제공하지 않는 모니터를 사용할때 Picker 를 이용해서 색상을 선택하면 나오는 문제랑 비슷한데요. 같은 색상 코드라고 해도 Color Space 에 따라서 다르게 표현되는거죠.

하지만 저는 Xcode 에서 Generic RGB를 맞게 설정하고 hex 코드를 입력했음에도 원하는 색상이 표시되지 않았습니다.

xcode 색상 선택기
xcode 색상 선택기

그래서 혹시나 Xcode 가 저기에 지정한 Color Space 를 무시하고 자기 맘대로 하는건 아닐까 싶어서 인터페이스 빌더 파일을 소스보기로 봤더니 역시나 Color Space 를 자기 마음대로 지정하고 있었습니다. Generic RGB 가 당연히 일반 RGB 스페이스라고 생각했는데 이건 애플의 버그 아닌가 싶어요…

- <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="calibratedRGB"/>
+ <color key="textColor" red="0.45098039215686275" green="0.4823529411764706" blue="0.51764705882352935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>

위 코드의 윗부분은 색상이 잘못 나올때 이고 아랫 부분은 색상이 제대로 나올때 입니다. 보면 colorSpace 값이 calibratedRGB으로 지정 되었을때 색상이 잘못나오는것을 볼 수 있습니다.

그래서 colorSpace 는 custom 으로 변경하고 customColorSpace 를 sRGB로 변경해서 원하는 색상을 표현 할 수 있게 되었습니다.

검색하다보니 Xcode 8에서는 제가 변경한걸로 기본 스페이스가 바뀌었다는데 이상하게 새로 만든 파일에서 계속 이런식으로 적용되더라구요. 이거 때문에 디버깅 뷰 까지 열어가면서 색상 코드 디버깅 하기는 처음이었습니다.

전체 프로젝트에서 저런식으로 잘못 표시된 색상이 있을까 싶어 검색해봤더니 어마어마하게 많더군요… 그래서 아래 명령어로 한번에 변경했습니다.

$ grep -rl 'colorSpace="calibratedRGB"' 폴더/* | xargs sed -i '' 's/colorSpace="calibratedRGB"/colorSpace="custom" customColorSpace="sRGB"/g'

이렇게 해도 나중에 또 색상 지정할때 이런 문제가 발생할 수 있는 여지는 있습니다. 그건 바로 Color Picker 에서 색상을 지정해두고 사용했을때 그 색상을 사용하면 색상을 추가할때의 colorSpace 로 다시 변경됩니다. 그래서 저는 이런식으로 기존에 저장해두고 사용하던 색상을 다 덮어 버렸습니다.

색상 피커 저장된 색상 지움 ㅋ
색상 피커 저장된 색상 지움 ㅋ

지우는 방법을 몰라서 그냥 안쓸거 같은 색상으로 덮어 버렸어요.

http://stackoverflow.com/a/27283783 글을 보면 칼라 피커에서 hex 코드를 입력하면 Color Space가 원복된다는 이야기도 있네요… 제 문제도 이 경우에 해당하는것 같아요. 이런식이면 인터페이스 빌더에서 색상입력하지 말라는거나 다름없지 않나요…. RGB 코드 따로 입력하는게 얼마나 귀찮은데

애플의 이 정책은 나름 모니터마다 같은 색상을 보게하려는 의도라고는 하던데 이것 때문에 오히려 원하는 색상을 표시 못하는 경우가 더 많은거 아닌가 하는 생각이 들었습니다.

제가 제대로 이해하지 못하고 삽질을 해결한것 같아서 여전히 찝찝합니다. 더 좋은 방법을 알고 계신분은 댓글로 알려주세요 ~

덧. 아마도 이 문제는 아이맥이나 애플이 만든 모니터를 사용한다면 발생하지 않을것도 같아요. 색상 피킹하는 툴은 아이맥이나 애플이 만든 모니터에서는 괜찮았거든요…

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

iOS 삽질 : Unknown class xxx in Interface Builder file

아이폰 개발은 하루하루가 삽질의 연속이네요… 오늘의 삽질은 클래스 파일을 찾을수 없다고 하면서 앱이 종료(크래시)되는 현상을 고치면서 발생했습니다.

인터페이스 빌더의 ViewController에 새로 만든 VC를 지정 했고 개발 빌드까지 아무 이상이 없었습니다. 그런데 테스트 플라이트를 통해 받은 Production 빌드에서만 앱이 크래시 나길래 Xcode에 아이폰을 연결해 실행하니 앱이 종료될때 다음과 같은 메시지가 있었습니다.

daangn[19282:4846848] Unknown class _TtC6daangn15MyArticlesTabVC in Interface Builder file.
Could not cast value of type 'UIViewController' (0x1b453af60) to 'daangn.MyArticlesTabVC' (0x1072c0028).
daangn[19282:4846848] Could not cast value of type 'UIViewController' (0x1b453af60) to 'daangn.MyArticlesTabVC' (0x1072c0028).

MyArticlesTabVC는 분명히 존재하는 파일이고 UIViewController 하위 클래스가 맞습니다. 그리고 개발 빌드에서는 아무 문제가 없었죠 ~

제가 문제를 겪은 VC는 XLPagerTabStrip 라이브러리의 특정 VC를 상속받은 거였고 이와 비슷한 문제가 이슈에 등록되어 있었습니다.

Can’t connect ‘containerView’ and ‘buttonBarView’ outlets #141

상속받는 클래스가 Generic 클래스 인데 Xcode 문제로 인터페이스 빌더의 커스텀 클래스에는 Generic 클래스를 지정 할 경우 이와 같은 오류가 발생한다고 하네요. 해결은 간단하게도 AppDelegae의 didFinishLaunch 함수 제일 위쪽에 다음과 같은 코드를 추가해서 했습니다.

let _ = MyArticlesTabVC(nibName: nil, bundle: nil)

이문제는 이전에도 한번 경험해봐서 알고는 있었는데 그때는 개발 빌드에서도 앱이 종료 되었었는데 이번에는 개발빌드는 정상동작하고 Production 빌드에서만 문제가 발생해서 찾기 어려웠습니다.

참고문서

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

애플서버에서 권한허용 문제로 빌드 실패하는 경우

Xcode 8로 업데이트후 애플 서버에 빌드를 제출하면 잠시후 아래와 같은 메시지로 실패했다고 메일이 오는 경우가 있다. 애플 빌드서버에서 실패하는거라 실제로는 Xcode 8 이 아니라 iOS 10을 지원하는 경우 발생한다고 보는게 맞겠다.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSAppleMusicUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSContactsUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSCalendarsUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothPeripheralUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMicrophoneUsageDescription key with a string value explaining to the user how the app uses this data.

This app attempts to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSMotionUsageDescription key with a string value explaining to the user how the app uses this data.

이는 카메라나 달력등 개인정보 관련된 권한을 사용할때만 Info.plist에 설정하면 되는 코드인데 사용하지 않는데도 애플 빌드서버에서 이런 오류를 발생하는 경우가 있다.

확인해보니 애플서버에서는 빌드할때 개인정보 관련코드가 외부 라이브러리에 있더라도 이걸 체크해서 무조건 위와 같이 오류를 발생한다.

나는 PermissionScope 라는 라이브러리를 사용하는데 이거는 앱 사용자에게 현재 권한을 보여주고 쉽게 권한을 요청하기에 좋은데 라이브러리 특성상 모든 개인정보 권한 코드를 가지고 있다. 라이브러리 사용할때 특정 개인정보는 사용하지 않으면 사용자가 볼일이 없는데도 라이브러리에 코드가 있다는 이유로 애플 빌드서버에서는 이런 오류를 발생시킨다.

이 문제를 고치려면 이와 같은 라이브러리를 사용하지 않거나 위에 언급된 키들에 대해 모두 문자열을 추가하면된다. 그런데 라이브러리에서 내부적으로 사용하지 않는 코드가 들어있을수도 있는데 이걸 다 어떻게 체크하나 ㅜㅜ 애드몹 같은 경우도 달력이나 몇개의 개인정보에 접근하는 코드가 있는데 이건 개발자가 적용할때 안쓴다고 하면 안쓰는건데…. 그래서 그냥 속편하게 모든 키를 다 추가하면 된다. 어차피 사용자가 안볼 문구이니까

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^

Xcode 8 에서 쓸모없는 로그 출력안하게 하기

Xcode 8을 설치하고 시뮬레이터에서 앱을 실행하면 이전과 달리 엄청난 로그들을 만나게 됩니다. 대략 아래와 같은 로그들이죠…

2016-09-20 18:43:53.720846 daangna[49804:209191] [] nw_endpoint_flow_attach_protocols [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Attached flow protocol
2016-09-20 18:43:53.720996 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.721119 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.725114 daangna[49804:209191] [] nw_socket_handle_socket_event Event mask: 0x800
2016-09-20 18:43:53.725334 daangna[49804:209191] [] nw_socket_handle_socket_event Socket received CONNECTED event
2016-09-20 18:43:53.725523 daangna[49804:209191] [] nw_socket_setup_notsent_lowat Set TCP_NOTSENT_LOWAT(16384)
2016-09-20 18:43:53.725712 daangna[49804:209191] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Output protocol connected
2016-09-20 18:43:53.726224 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.726454 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Connected path is satisfied
2016-09-20 18:43:53.726645 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.726839 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.727092 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.727326 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 ready resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.727563 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:changed_viability
2016-09-20 18:43:53.727758 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:changed_viability
2016-09-20 18:43:53.728074 daangna[49804:209175] [] __tcp_connection_start_block_invoke 6 sending event TCP_CONNECTION_EVENT_CONNECTED in response to state ready and error (null)
2016-09-20 18:43:53.728255 daangna[49804:209175] [] tcp_connection_event_notify 6 event: TCP_CONNECTION_EVENT_CONNECTED, reason: nw_connection event, should deliver: true
2016-09-20 18:43:53.728673 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.728852 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Using CoreTLS
2016-09-20 18:43:53.729044 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS client queue
2016-09-20 18:43:53.729243 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS prepare handler
2016-09-20 18:43:53.729432 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Set custom TLS message handler
2016-09-20 18:43:53.729621 daangna[49804:209175] [] nw_endpoint_start_tls_while_connected [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Attached TLS protocol to connected flow
2016-09-20 18:43:53.729784 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 ready resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.730001 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_secondary_connect
2016-09-20 18:43:53.730186 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:start_secondary_connect
2016-09-20 18:43:53.730400 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.730580 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.730777 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:start_connect
2016-09-20 18:43:53.730938 daangna[49804:209175] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Transport protocol connected
2016-09-20 18:43:53.731079 daangna[49804:209175] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 in_progress socket-flow (satisfied)]
2016-09-20 18:43:53.731277 daangna[49804:209175] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] reported event flow:finish_transport
2016-09-20 18:43:53.731424 daangna[49804:209175] [] nw_connection_endpoint_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] reported event flow:finish_transport
2016-09-20 18:43:53.746985 daangna[49804:209191] [] nw_endpoint_flow_protocol_connected [6.1 31.13.68.12:443 in_progress socket-flow (satisfied)] Output protocol connected
2016-09-20 18:43:53.747630 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.747850 daangna[49804:209191] [] nw_endpoint_flow_connected_path_change [6.1 31.13.68.12:443 ready socket-flow (satisfied)] Connected path is satisfied
2016-09-20 18:43:53.748068 daangna[49804:209191] [] nw_endpoint_resolver_receive_report [6 graph.facebook.com:443 in_progress resolver (satisfied)] received child report:[6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:43:53.748310 daangna[49804:209191] [] nw_connection_endpoint_report [6.1 31.13.68.12:443 ready socket-flow (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.748607 daangna[49804:209191] [] nw_connection_endpoint_report [6 graph.facebook.com:443 ready resolver (satisfied)] reported event flow:finish_connect
2016-09-20 18:43:53.748830 daangna[49804:209175] [] __tcp_connection_start_block_invoke 6 sending event TCP_CONNECTION_EVENT_TLS_HANDSHAKE_COMPLETE in response to state ready and error (null)
2016-09-20 18:43:53.749050 daangna[49804:209175] [] tcp_connection_event_notify 6 event: TCP_CONNECTION_EVENT_TLS_HANDSHAKE_COMPLETE, reason: nw_connection event, should deliver: true
2016-09-20 18:43:53.749295 daangna[49804:209175] [] tcp_connection_get_statistics DNS: 3ms/4ms since start, TCP: 10ms/17ms since start, TLS: 18ms/34ms since start
2016-09-20 18:44:07.686676 daangna[49804:209118] subsystem: com.apple.UIKit, category: Touch, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:07.687387 daangna[49804:209118] subsystem: com.apple.UIKit, category: Gesture, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:07.688917 daangna[49804:209118] subsystem: com.apple.UIKit, category: GestureExclusion, enable_level: 0, persist_level: 0, default_ttl: 1, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0
2016-09-20 18:44:58.968517 daangna[49804:209882] [] nw_socket_handle_socket_event Event mask: 0x2
2016-09-20 18:44:58.968960 daangna[49804:209883] [] tcp_connection_cancel 6
2016-09-20 18:44:58.969395 daangna[49804:209882] [] nw_socket_handle_socket_event Socket received READ_CLOSE event
2016-09-20 18:44:58.969753 daangna[49804:209882] [] nw_endpoint_handler_cancel [6 graph.facebook.com:443 ready resolver (satisfied)]
2016-09-20 18:44:58.970003 daangna[49804:209882] [] nw_endpoint_handler_cancel [6.1 31.13.68.12:443 ready socket-flow (satisfied)]
2016-09-20 18:44:58.970305 daangna[49804:209882] [] nw_endpoint_flow_protocol_disconnected [6.1 31.13.68.12:443 cancelled socket-flow (null)] Output protocol disconnected
2016-09-20 18:44:58.970517 daangna[49804:209882] [] nw_resolver_cancel_on_queue 0x618000103210
2016-09-20 18:44:58.970791 daangna[49804:209882] [] -[NWConcrete_tcp_connection dealloc] 6

이 로그들을 없애려면 “Edit Scheme” 에서 “Run” > “Arguments” > “Environment Variables” 에 다음과 같은 값을 추가해야합니다.

Name 에는 OS_ACTIVITY_MODE 를 추가하고 Value 에는 disable 을 입력후 왼쪽 체크박스를 체크한 상태로 둡니다.

Xcode 8 log fix
Xcode 8 log fix

이 현상은 애플이 의도적으로 넣은건지 아닌지 모르겠지만 꽤나 귀찮습니다.

참고자료

게시글의 아마존, iTunes 링크들을 통해 구매를 하시면 제휴(Affiliate) 프로그램에 의해 저에게 일정 금액이 적립될 수 있습니다. ^_____^