월별 글 목록: 2017년 4월월

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 10.3 앱 아이콘 동적으로 변경하기

iOS 10.3에 예상치 못한 신규기능중 앱 아이콘을 변경하는 기능이 있습니다. 이번글에서는 이 신규기능을 활용하는 방법과 적용하지 못하는 경우를 적어볼려고 합니다.

MLB At Bat 앱에서 이 기능을 적용했는데요. 자신이 좋아하는 팀 로고로 앱 아이콘을 변경 할 수 있게 했습니다.

앱 아이콘 변경

MLB At Bat 앱 아이콘 변경

이 포스트의 내용은 영상으로도 촬영 했으니 영상도 참고하시면 동작하는 방식을 이해하기 더 쉬우실거에요

기능을 적용하는것은 정말 간단합니다. 우선 기존에 있던 프로젝트에 새로 추가할 앱 아이콘 이미지를 추가합니다. 예시의 경우 daangn_jeongja@2x.png, daangn_jeongja@3x.png 파일을 추가했습니다.

앱 아이콘 추가

앱 아이콘 추가

여기서 이미지를 Assets.xcassets 에 추가하면 안됩니다. 에셋에 추가해서 사용하고 싶어서 시도를 해봤는데 동작하지 않는것을 확인했습니다.

이미지를 추가 했으면 Info.plist 파일에 추가한 아이콘 이미지에 대한 정보를 등록합니다.

Info.plist 파일에 추가할 내용

Info.plist 파일에 추가할 내용

Xcode 의 Property Editor 에서 자동 완성되지 않는 항목들이라 위와 같은 구조로 일일이 입력해줘야 하는 번거로움이 있습니다.

“Icon files (iOS 5)” 키를 추가하고 그 밑에 “CFBundleAlternateIcons”를 “Dicionary” 타입으로 추가합니다. 그리고 나서 앞으로 변경 가능한 앱 아이콘 후보 이름을 적어 주는데요. 위의 경우 “bundang”, “jeongja” 라는 2개의 앱 아이콘 후보를 추가했습니다.

그리고 각각의 앱 아이콘 후보 이름 밑에는 “CFBundleIconFiles” 라는 “Array” 타입의 키를 추가하고 해당 항목 밑에 실제 앱 아이콘 후보의 이미지 파일명을 적으면 됩니다.

여기서 중요한건 실제 추가한 앱의 파일 이름과 소스코드에서 지정할 파일 이름이 달라도 된다는건데요. 위의 경우 실제 추가한 이미지 파일은 daangn_jeongja@2x.png 인데 이 파일에 해당하는 소스코드에서 참고할 이름은 “jeongja” 입니다.

Info.plist 에 추가한 항목의 XML 소스코드는 다음과 같습니다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
   <key>CFBundleIcons</key>
   <dict>
     <key>CFBundleAlternateIcons</key>
     <dict>
     <key>bundang</key>
     <dict>
       <key>CFBundleIconFiles</key>
         <array>
           <string>daangn_bundang</string>
           <string>daangn_bundang-72</string>
           <string>daangn_bundang-Small</string>
         </array>
       </dict>
     <key>jeongja</key>
     <dict>
       <key>CFBundleIconFiles</key>
       <array>
         <string>daangn_jeongja</string>
         <string>daangn_jeongja-72</string>
         <string>daangn_jeongja-Small</string>
       </array>
       </dict>
     </dict>
   </dict>
.... 생략

이제 소스코드에서 앱 아이콘을 변경하고자 하는 부분에 아래 코드를 입력합니다

UIApplication.shared.setAlternateIconName("jeongja") { (error) in
    debugPrint("error \(error)")
}

위 코드가 실행되면 앱 아이콘이 변경됩니다. 마지막에 실행되는 코드 블럭은 실행완료후에 호출되는데 error 가 nil 인 경우 성공한것이고 그렇지 않은 경우 변경에 실패한것입니다.

앱 아이콘 변경 확인

앱 아이콘 변경 확인

앱 아이콘 변경코드가 호출되면 위와 같이 사용자에게 알려주는 팝업이 실행되는데요. 이 팝업은 무조건 보여지는 거라 보이지 않도록 할 방법은 없습니다.

앱 아이콘 변경을 하다보면 안되는 경우를 몇가지 발견하게 되는데요.

첫째, 메인스레드에서 코드가 호출되지 않는 경우 앱 아이콘 변경이 되지 않습니다. 메인스레드가 아닌경우 complete 블럭에서 error에 다음과 같은 객체가 전송됩니다.

"Error Domain=NSCocoaErrorDomain Code=3072 \"작업이 취소되었습니다.\""

아이폰 시스템 언어가 영어일 경우 아래 메시지

"Error Domain=NSCocoaErrorDomain Code=3072 \"The operation was cancelled.\""

이때는 메인 스레드에서 실행되도록 다음과 같이 수정합니다.

DispatchQueue.main.async {
  UIApplication.shared.setAlternateIconName("jeongja") { (error) in
    debugPrint("error \(error)")
  }
}

둘째, 앱이 foreground 에서 실행중이지 않을때는 앱 아이콘을 변경할 수 없습니다. 이는 사용자 몰래 앱 아이콘이 변경되는것을 막기위한 조치라고 생각됩니다.

셋째, 에셋으로 이미지 추가가 안되다보니 아이패드를 지원하는 경우 예전에 아이콘 추가하던 방식대로 앱 아이콘 이미지 파일의 이름을 맞춰야합니다. 위의 예시에서 daangn_jeongja-72 라고 하는식으로 뒤에 suffix 를 예전에 하던대로 맞춰야하는거죠. 파일 이름 규칙은 애플문서(App Icons on iPhone, iPad and Apple Watch)를 참고 하세요.

참고자료

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

iOS 삽질 : 릴리즈 빌드에서만 런타임에 에러 발생

앱을 만들다가 Debug 에서 잘 실행되는것을 확인하고 TestFlight 에 올려서 테스트 하는데 앱을 시작하자 마자 크래시가 발생하는 문제가 발생했습니다.

발생한 에러는 다음과 같았습니다

2017-04-03 15:12:19.049 daangn[48068:1047076] Unknown class _TtC6daangn17NotificationTabVC in Interface Builder file.
Could not cast value of type 'UIViewController' (0x112194288) to 'daangn.NotificationTabVC' (0x10bdc09c8).

이렇게 저렇게 해결 방법을 찾다보니 Swift 의 Release Scheme의 Optimization Level 을 ‘Fast, Whole Module Optimization’ 에서 None 으로 변경하면 잘된다는 글을 발견하고 그대로 해봤더니 정말이지 Release scheme 에서도 에러가 발생하지 않았습니다.

좀더 정확히는 ‘Fast, Whole Module Optimization’ 이 아닌 ‘None’, ‘Fast, Single-File Optimization’ 로 변경하면 앱이 종료되지 않았습니다.

Optimization 의 버그 인가 싶어서 에러 메시지에서 못찾는다고 나온 클래스만 별도의 IB 로 분리하거나 스토리보드로 분리해보고 Derived Data 도 지우고 Clean Build 도 해봤지만 문제는 해결되지 않았습니다.

이렇게 하루내내 삽질을 하다가 원인을 찾았는데 그건 제가 사용하는 라이브러리의 문제 였습니다. 저는 좌우로 스크롤되는 VC를 구현하기위해 XLPagerTabStrip 이라는 라이브러리를 사용하는데 이 라이브러리에서 사용하는 코드 구현 방식에서 어쩔수 없이 오류가 발생하는 부분이 있었습니다.

해결 방법은 생각보다 간단한데요. AppDelegate 에서 앱이 시작되고 XLPagerTabStrip 을 사용하는 코드가 호출되기 전에 XLPagerTabStrip 을 사용하는 VC를 한번 호출해주면됩니다.

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

위와 같이 한번 호출해주면 실제 사용할때 에러가 발생하지 않습니다. 이 에러는 이슈에도 올라와 있는데 Swift 의 Interface Builder 의 Generic 지원 문제로 인해 발생하는 문제 였습니다.

이런 삽질은 할때 마다 너무 힘드네요. 안그래도 Swift 빌드 느린데 빌드를 몇십번씩 하면서 테스트 하려니 중간에 다른것도 못하고 멍하니 화면만 바라보면서 이번엔 잘되라 하면서 기도하고 있으니 시간도 아깝구요.


진정한 삽질이란 이런걸까요? 작년 11월의 제가 이와 동일한 문제를 이 블로그에 iOS 삽질로 적었었네요… 글 다 작성하고 나서 아래 관련게시글로 나와서 알게됬습니다. ㅜㅜ https://code.iamseapy.com/archives/36

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