2015년 2월 9일 월요일

Swift 예제 - HEX 색상코드를 UIColor로 변환하기

예제로 보는 Swift 두 번째 포스팅. 이번에는 웹페이지에서 쓰이는 16진수 컬러코드를 UIColor로 변환하는 방법을 쓸 데 없이 길게 풀어써 본다.

참고로 이 글은 플레이그라운드(Playground)에서 작성한다는 가정 하에 쓰여진 글이다.

입력의 정의

웹에서 쓰이는 16진수 컬러코드는 #으로 시작하는 7자리 혹은 4자리 문자열이다. 아래가 그 예이다.
  • #ff240d: Red 0xFF, Green 0x24, Blue 0x0D
  • #0cb: Red 0x00, Green 0xCC, Blue 0xBB
이 컬러 코드는 Red, Green, Blue 세 가지 수치로 구성되어 있고 각각 16진수 1바이트(UInt8) 형태이다. 즉 10진수로 표현하자면 최소 0에서 최대 255까지의 수치로 각 컬러 컴포넌트를 표현하도록 구성되어 있다.

필요한 출력

UIColor를 RGB 수치를 이용해 생성 할 때는 아래와 같은 생성자를 이용 할 수 있다.
UIColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
그런데 이 생성자를 이용 할 경우 웹에서 쓰이는 16진수 컬러코드를 그대로 사용 할 수가 없다. 왜냐하면 각 컬러 컴포넌트는 실수형, 즉 0.0이 최소이고 1.0이 최대이기 때문에 16진수로 1바이트로 표현하는 정수형 타입을 그대로 넣을 수가 없기 때문이다.

문제를 해결하기

문제는 이미 정의가 되었다. 16진수 문자열로 표시된 각 컬러 컴포넌트를 실수형으로 바꾼뒤 이 실수 수치를 이용해 UIColor를 생성 해 내면 된다.

해결 절차를 두 가지로 크게 구분해 보자.
  • 16진수 문자열을 각 컬러 컴포넌트로 분리해 내기
  • 분리된 16진수 문자열 컬러 컴포넌트를 실수로 변경해서 UIColor 생성하기

입력 예제

예제로 아래와 같은 입력을 만들어 두었다.
let hexColorCode = "#ff230d"
let shortHexColorCode = "#0cb"
이제 이 문자열을 UIColor로 바꾸어 보자.

문자열 분리해내기

앞서 이야기 한 대로, 웹 컬러 코드는 '#' 으로 시작하는 16진수를 문자열로 표현한 문자열이다. 그리고 길이는 4 혹은 7이다. 이걸 각각의 컴포넌트 문자열로 분리해 보자.
// 문자(Character) 두 개를 하나의 문자열로 합치는 오퍼레이터
func +(left: Character, right: Character) -> String {
    return "\(left)\(right)"
}

func splitHexColorCode(hexColorString: String) -> [String]? {
    // 문자열을 배열화 하면 문자 배열([Character])이 된다.
    let array = Array<Character>(hexColorString)
    var result = [String]()

    if array.count == 7 {
        result.append(String(array[0]))
        result.append(array[1] + array[2])
        result.append(array[3] + array[4])
        result.append(array[5] + array[6])

        return result
    }
    else if array.count == 4 {
        result.append(String(array[0]))
        result.append(array[1] + array[1])
        result.append(array[2] + array[2])
        result.append(array[3] + array[3])

        return result
    }
    else {
        return nil
    }
}
splitHexColorCode 함수가 웹 컬러 코드를 문자열 배열로 쪼게어주는 함수이다. 인자로 웹 컬러 코드 문자열을 받고, 반환으로 문자열 배열을 옵셔널로 리턴한다. 옵셔널인 이유는 입력되는 문자열의 길이가 4 혹은 7 길이가 아니면 문제 정의에 어긋나기 때문에 nil을 리턴시키기 위함이다.

이 함수 위에 선언된 '+' 오퍼레이터 오버로드는 문자(Character) 타입 끼리 결합해서 문자열로 만들기 위함이다. 오퍼레이터 오버로드는 이런 상황에서 좀 더 편하고 가독성 높은 문법을 가질 수 있게 도와준다.

이 코드를 테스트 해 보자.
splitHexColorCode(hexColorCode)         // {["#", "ff", "23", "0d"]}
splitHexColorCode(shortHexColorCode)    // {["#", "00", "cc", "bb"]}
의도대로 잘 돌아간다.

UIColor 생성

16진수 1바이트(UInt8)를 실수형으로 바꾸기는 쉽다. 그냥 최대 수치인 255로 나눠주면 된다. 다만 입력은 문자열로 구성된 16진수 데이터이다. 이를 실수형으로 바꾸려면 우선 정수로 뽑아내야 한다.

16진수 문자열에서 정수 뽑아내기를 NSScanner를 이용해 구현해 보자.
func hexString2CGFloat(hexString: String) -> CGFloat {
    let scanner = NSScanner(string: hexString)
    var intValue: UInt32 = 0
    scanner.scanHexInt(&intValue)

    return CGFloat(CGFloat(intValue) / 255.0)
}
NSScanner의 scanHexInt 메서드를 이용해 정수 값을 추출해 내고 이 값을 다시 255를 나눈 실수형 타입으로 리턴하는 함수이다.

이제 이 함수를 이용해 각 실수형 컬러 컴포넌트를 만들고 UIColor를 생성하는 함수를 만들어 보자.
func hexString2UIColor(hexString: String) -> UIColor? {
    if let hexStrings = splitHexColorCode(hexString) {
        let r = hexString2CGFloat(hexStrings[1])
        let g = hexString2CGFloat(hexStrings[2])
        let b = hexString2CGFloat(hexStrings[3])

        return UIColor(red: r, green: g, blue: b, alpha: 1.0)
    }
    return nil
}
마지막으로 이 함수가 잘 동작하는지 테스트 해 보자.
hexString2UIColor(hexColorCode)      // {Some r 1.0 g 0.137 b 0.051 a 1.0}
hexString2UIColor(shortHexColorCode) // {Some r 0.0 g 0.8 b 0.733 a 1.0}
잘 되는 것 같다.

확장(Extension)으로 구현해보기

지금까지의 함수로 구현했는데 이 함수는 UIColor를 생성하는데만 사용 할 테니 UIColor의 메소드로 만들어지면 좋겠다는 생각이 들었다. 그래서 UIColor 클래스를 확장(Extension)해 보는 방식으로 바꿔봤다.
extension UIColor {
    class func colorWithWebHexString(webHexString: String) -> UIColor? {
        if let hexStrings = splitHexColorCode(webHexString) {
            let r = hexString2CGFloat(hexStrings[1])
            let g = hexString2CGFloat(hexStrings[2])
            let b = hexString2CGFloat(hexStrings[3])

            return UIColor(red: r, green: g, blue: b, alpha: 1.0)
        }
        return nil
    }
}
hexString2UIColor 함수를 완전히 동일한 모양으로 UIColor의 확장 메소드로 만들었다. 그리고 이 메소드는 굳이 인스턴스화 하지 않고도 실행시킬 수 있도록 class method로 만들었다.

이제 테스트 해 보자.
UIColor.colorWithWebHexString(hexColorCode)
    // {Some r 1.0 g 0.137 b 0.051 a 1.0}
UIColor.colorWithWebHexString(shortHexColorCode)
    // {Some r 0.0 g 0.8 b 0.733 a 1.0}
동일한 결과가 나오는 걸로 봐서 잘 돌아간다고 할 수 있다.

정리 및 관련내용

지금까지 웹 컬러 코드를 이용해 UIColor 인스턴스를 생성하는 방법에 대해 살펴봤다. 지금까지 가지고 있는 지식만으로 구현을 하다보니 이 보다 더 좋은 방법이 있는지 확실치는 않지만, 아마도 더 간단하게 구현하는 방법이 있으리라 생각된다.

이 예제에서 사용된 정의에 대해 자세한 내용을 알고 싶다면 상세 내용으로 이어지는 링크를 참고하자.

댓글 없음 :