Swift - 한글 Typo animation

Zion·2022년 1월 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개의 댓글