Swift - 한글 Typo animation

Zion·2022년 1월 1일

한글 Typo animation

타이포 애니메이션을 하려고 한다.
그런데 ?! 한글은 영어와 다르게 초성, 중성, 종성이 있다.
영어처럼 한자씩 찍어내면 안된다는건 아닌데 ... 내가 하고싶은건 글자 만들어지는 과정,

즉 예를들어
'확실' 이란 글잘

확 확실

이렇게 찍히는게 아니라

ㅎ 화 확 확ㅅ 확시 확실

이렇게 찍고 싶다 ~ 이말이다.
그러려면 한글의 유니코드를 쪼개봐야한다.
가보자고.

Unicode

관련 문서 : String and Characters - Swift.docs


decomposed 를 보면 각각 ㅎ, ㅏ, ㄴ에 해당하는 유니코드들을을 붙여서 써줘도 print는 조합한 글자로 찍힌다.

아항 우린 일단 글자를 입력 받으면 초성, 중성, 종성에 해당하는 유니코드로 분해해줘야 한다.

유니코드에서 한글은 0xAC00 ~ 0xD7A3(44032 ~ 55203 : 10진수) 사이의 코드값을 갖는다.(총 11,172개)
유니코드 내 한글은 초/중/종성의 각 음소의 조합으로 표현된다.
즉 초성 19개, 중성 21개, 종성 28개를 조합하여 하나의 글자가 되는 것이다.
따라서 각 초,중,종성에 해당하는 한글자모의 위치값을 계산하여 최종적으로 만들어지는 글자의 코드를 생성할 수 있다.
이 때 들어가는 값은 위치값으로 '0 ~ (해당 음소의 개수) - 1'만큼의 인덱스를 의미한다.

( ( 초성 X 21 ) + 중성 ) X 28 + 종성 + 0xAC00

  • 각 음소의 index
초성idx = ((문자코드 - 0xAC00) / 28) / 21

중성idx = ((문자코드 - 0xAC00) / 28) % 21

종성idx = (문자코드 - 0xAC00) % 28

index가 유니코드 값은 아니다. 각각 음소의 시작값으로부터 얼마나 떨어졌는지(=offset)이다.

  • 각 음소의 유니코드
    초성의 유니코드 시작값은 0x1100,
    중성은 0x1161,
    종성은 0x11A8
    이므로 이를 각각 더한다.
    ( 종성의 경우, 받침이 없는 문자의 경우가 있기 때문에 종성에는 1을 뺀다.)
초성의 유니코드 = 초성idx + 0x1100

중성의 유니코드 = 중성idx + 0x1161

종성의 유니코드 = 종성idx + 0x11A8 - 1

    func diassembleUnicode(_ char: UInt32) -> [UnicodeScalar] {
        
        let x = (char - 0xac00) / 28 / 21
        let y = (char - 0xac00) / 28 % 21
        let z = (char - 0xac00) % 28
        
        let initial = UnicodeScalar(0x1100 + x)// 초성
        let neuter = UnicodeScalar(0x1161 + y)// 중성
        let final = UnicodeScalar(0x11a7 + z)// 종성
        
        var arr = [initial, neuter, final].compactMap { $0 }
        
        if final == UnicodeScalar(0x11A7) { //받침 없음
            arr.removeLast()
        }
        
        return arr
    }

받침이 없다면 ... array에서 지워줬다.

Typo animation func.

1 try

let string = 글자
for i in string {
    if let unicodeVal = UnicodeScalar(String(i))?.value {
        let arr = diassembleUnicode(unicodeVal)
        arr.forEach{ char in
        글자 += "\(char)"
        self.letterLabel.text! = 글자
    }
    RunLoop.current.run(until: Date() + 0.04)
}

음... 이렇게 짜봤는데

띠용 ...

콘솔창엔 원하는대로 '음소'씩 찍히는데 UI가 한 '글자'씩 찍힌다?!

solution)
눈보다 빠르게 업데이트 되는것!
그러므로 RunLoopLabel업데이트 할 때도 주자!

let string = 글자
for i in string {
    if let unicodeVal = UnicodeScalar(String(i))?.value {
        let arr = diassembleUnicode(unicodeVal)
        arr.forEach{ char in
        글자 += "\(char)"
        RunLoop.current.run(until: Date() + 0.04)
        self.letterLabel.text! = 글자
    }
    RunLoop.current.run(until: Date() + 0.4)
}

실행 화면

실행화면과 같은 Typo animation 만든 기록이다.

보다 자연스러운 시각 효과를 위한다면 RunLoop의 시간 값은 적당한 범위 내에서 random으로 주면된다.

참고

https://soooprmx.com/unicodescalar/?amp
https://zeddios.tistory.com/493

피드백 환영합니다.

profile
어제보다만 나아지는

0개의 댓글