HIT해

[iOS/SceneKit] 3D 화면에 텍스트 출력하기 본문

Swift/UIKit 개발 노트

[iOS/SceneKit] 3D 화면에 텍스트 출력하기

힛해 2024. 9. 4. 02:56
728x90

이전 포스팅 영상을 본 사람이 있다면 궁금했을 수도 있다.

 

3D 펫말에 어떻게 텍스트를 집어넣지???

 

나도 어떻게 집어넣을까 많은 고민을 했다..

 

ZStack에 2D 텍스트를 기울여지게 띄워놓을까..? 

-> 회전했을때 텍스트가 붕 뜨게 될 것이다.

 

찾아보니 SceneKit에 3D Text를 출력하는 기능이 있었다. ( 아니 이런건 왜 있는거야.. )

 

두가지 방법이 있었다.

  1. CATextLayer로 구현하기
  2. SCNText로 구현하기

SCNText가 더 최신 기술이고 메서드 사용도 더 용이해보여 SCNText로 해결해보기로 했다.

 

https://developer.apple.com/documentation/scenekit/scntext

 

SCNText | Apple Developer Documentation

A geometry based on a string of text, optionally extruded to create a three-dimensional object.

developer.apple.com

 

한번 사용해보자!

 

SCN 기능들은 대부분 비슷해서 별다른 공부없이도 바로 사용할 수 있었다.

 

코드를 보고 기능들을 살펴보자

let scnText = SCNText()
            scnText.font = UIFont(name: "Helvetica Neue", size: 8)!
    
            scnText.firstMaterial?.diffuse.contents = UIColor.black
            scnText.containerFrame = CGRect(origin: .zero, size: CGSize(width: 70, height: 60))
            scnText.string = ""
            scnText.isWrapped = true
            scnText.extrusionDepth = 1.2
            
             // flatness : 텍스트의 부드러움을 결정함
             scnText.flatness = CGFloat(1)
             
             // SCNNode 생성
             let textNode = SCNNode(geometry: scnText)
    
             textNode.eulerAngles = SCNVector3(0, 0, 0) // 텍스트가 정면을 바라보도록
             textNode.scale = SCNVector3Make(0.01, 0.01, 0.01)
             textNode.name = "text"
             
             return textNode

 

string 값이 표시할 텍스트 값이고

 

isWrapped는 줄바꿈을 하겠다는 뜻이고 이는 

 

containerFrame 에 정의된 가로 길이를 넘어가면 줄을 바꾸게 된다.

 

flatness의 CGFlaot 값에 따라 부드러움을 정하고

 

extrusionDepth는 3D 텍스트이다보니 Z축 기준으로 얼마나 입체적이게 할지 그 크기를 정의하는 부분이다.

 

사실 간단한 코드지만 프로젝트에 적용하는데에는 어려움이 있었다...

 

containerFrame을 적용하면 텍스트의 크기가 컨테이너에 맞게 조정되는데 이때 추가 scale 조정이 없으면 화면에 아무런 텍스트가 표시되지않았다..

 

그래서 단순히 텍스트 표시 기능은 10분만에 구현했지만 이 줄바꿈 기능 때문애 몇시간이나 날렸고..

 

CATextLayer로 거의 다 구현을 마쳤다가 텍스트가 로드되는게 마음에 들지 않아 다시 SCNText로 돌아가고..

 

( 혹시 CATextLayer로 구현하고 싶은 사람은 포스팅 맨 아래의 코드를 사용하길 바란다... )

 

https://developer.apple.com/forums/thread/85368

이 링크의 답변 덕분에 추가적인 시간 낭비를 막을 수 있었다..

 

이렇게 하면 3D View에 텍스트가 추가되는데 가변적인 텍스트이기에 어디에서 값을 변경해야할 지 고민이 되었다.

 

makeViewupdateView

 

makeView에서 생성을 하면 추가적인 로직이 없는이상 정의된 값을 바꿀 수 없었고,

 

updateView에서 생성을 하면 값을 변경할때마다 중복으로 노드가 생겨서 오류가 발생했다.

 

그래서 makeView에서 빈 문자열의 3DText를 생성하고 updateView에서 3D Text Node의 String에 접근해 값을 바꿔주었다.

 

func updateTextNode(_ textNode: SCNNode, newText: String) {
    // 텍스트 노드에서 geometry를 SCNText로 변환
    if let scnText = textNode.geometry as? SCNText {
        // 텍스트 내용을 변경
        scnText.string = newText
    } 
}

 

이렇게 받는 타입을 textNode라고 지정해야 String값을 변경할 수 있으니 참고하길 바란다.

 

updateTextNode(텍스트노드, newText: TCA바인딩값)

 

이렇게 하면 3D 줄바꿈 텍스트 기능이 완성된다...!

 

 

 

완성이다...!

 

TCA최신기술과 한물간 SCN기술의 콜라보....

 

너무 즐겁다. SCN 기술을 원하는 회사 어디 없으려나...

 

 

 

 

 

아래는 포스팅에서 말한 CATextLayer 코드다

func addTextNode(above parentNode: SCNNode, text: String) -> SCNNode {
    // CATextLayer 생성
    let textLayer = CATextLayer()
    textLayer.string = text
    textLayer.fontSize = 16
    textLayer.foregroundColor = UIColor.black.cgColor
    textLayer.alignmentMode = .center
    textLayer.isWrapped = true
    
    // 텍스트 레이어의 크기 설정
    let maxWidth: CGFloat = 100
    let maxHeight: CGFloat = 50
    textLayer.frame = CGRect(x: 0, y: 0, width: maxWidth, height: maxHeight)
    
    // SCNPlane 생성
    let plane = SCNPlane(width: maxWidth / 100, height: maxHeight / 100)
    
    // SCNMaterial 생성 및 CATextLayer를 적용
    let material = SCNMaterial()
    material.diffuse.contents = textLayer
    plane.firstMaterial = material
    
    // 텍스트 노드 생성
    let textNode = SCNNode(geometry: plane)
    
    // 부모 노드의 위치 가져오기
    let parentPosition = parentNode.position
    
    // 텍스트 노드의 위치를 부모 노드의 위치로 설정
    textNode.position = SCNVector3(parentPosition.x, parentPosition.y + 1.0, parentPosition.z) // 부모 노드 위쪽에 텍스트를 배치합니다.
    
    // 부모 노드에 텍스트 노드 추가
    parentNode.addChildNode(textNode)
    
    return textNode
}