iyOmSd/Title: SwiftUI

[SwiftUI] 분리된 프레임워크(모듈) 리소스(Font, Color, Image)접근

냄수 2023. 2. 24. 19:52
반응형

개발하면서 경험했던 이슈를 공유겸 기록하려고합니다

Tuist를 사용해서 개발하면서 UI모듈을 나누게됬어요

UI가 분리되었으니 

리소스에 해당하는

이미지, 컬러, 폰트같은 요소들이 UI모듈에 정의되고

다른곳에서 꺼내쓰는 방식으로 사용하죠

 

위의 그림기준으로

Feature에서

평소와같이 UI를 import해서 사용하게된다면

실행에 오류는없지만

원하는 리소스를 가져올 수 없는걸 확인할 수 있어요

 

이유는

프레임워크를 분리했기때문에

번들이 분리되어있기때문이에요

 

 

먼저 컬러와 이미지에 대해서 정리할게요

결론부터 말하자면 컬러와 이미지는 해결방법이 동일해요

Bundle을 정하고 가져오는 방식이에요

 

우선 일반적으로 UI모듈에 정의된 컬러에 접근하게된다면

아래와 같은 로그를 만날 수 있습니다.

No color named 'nsColor' found in asset catalog for main bundle

// UI모듈
public extension Color {
    static var nsColor: Color { Color("nsColor") }
}

 

// Main 모듈
struct MainView: View {
    var body: some View {
        VStack {
            Color.nsColor.frame(height: 100)
            Text("Hello, World!")
        }
        .border(.red)
    }
}

색이 아무것도 안뜨는걸 볼 수 있어요

앱을 실행하면 main으로 시작하면서 리소스를 불러올 때 기본적으로 main번들에서 가져오려고하죠

하지만 UI모듈은 다른 프레임워크고 고유한 identifier도 있고, main과 다른번들을 사용해요

 

모듈을 만들때

타겟을만들면서 다 정의해줬죠

UI번들에 있는 컬러로 접근하기위해 UI번들아이디를 이용해서

아래와같이 코드를 작성하면

정상적으로 색이 뜨는걸 볼 수 있어요

public extension Color {
    static var nsColor: Color {
        Color("nsColor", bundle: Bundle(identifier: "ns.NSWorkspace.UI"))
    }
}

 

 

 

UI모듈에 폰트를 정의해두고 사용하는 방법

폰트도 마찬가지로

정의를 해서 잘 썼지만

실제로 구현한 UI를 보면 폰트가 쏙 빠져서 미워보이는 경우를 만나실수있습니다

 

 

폰트를 프로젝트파일에 끌어다놔서 저장돼있고, plist설정 했다 가정하고 시작할게요

for family in UIFont.familyNames.sorted() {
    let names = UIFont.fontNames(forFamilyName: family)
    print("Family: \(family) Font names: \(names)")
}

우선 위의 코드를 실행시켜서

폰트가 존재하고

이름을 올바르게 사용했는지 확인해야해요

파일이름과 내부에서 사용하는 이름이 다를수 있어요

만약에 폰트가 제대로 적용됬다면

로그에 이렇게

"Pretendard-Regular" 이라고 뜨고

사용할땐 이 문자열을 그대로 넣어야해요

 

하지만

UI모듈에 폰트를 정의해두고

메인모듈에서 사용한다면

폰트가 적용되지 않는 현상을 볼 수 있어요

위코드로 로그를 찍어도 보이지않죠

Font는 Bundle로 접근해서 가져올 수가 없는데

어케하지...?

 

방법이 다 있었습니다

CTFontManagerRegisterFont~~~ 함수가 있어요

 

CoreText...!

폰트매니저에게 폰트를 등록시켜준다네요

 

여러개중 하나를 골라봤는데 ~ForURL 함수는 입력값으로 

url: 폰트가 존재하는 URL

scope: 수명을 정의함

process - 세션유지기간동안 사용가능

persistent - 등록을 해제하지않으면 계속사용가능

이렇게 있네요

 

Font Type을 정의해서 썼기때문에

해당 타입을 순회하면서

UI모듈 번들에 접근해서

각 폰트파일의 URL을 가져와서

등록시키는 방식으로 해결할 수 있어요

public extension Font {
    enum PretendardWeight: String, CaseIterable {
        case regular = "Pretendard-Regular"
        case medium = "Pretendard-Medium"
        case semiBold = "Pretendard-SemiBold"
        case bold = "Pretendard-Bold"
        case black = "Pretendard-Black"
    }
    
    static func pretendard(size: CGFloat, weight: PretendardWeight = .regular) -> Font {
        .custom(weight.rawValue, size: size)
    }
    
    static func registerFont() {
        Font.PretendardWeight.allCases.forEach {
            guard let url = Bundle.uiBundle?.url(forResource: "\($0.rawValue)", withExtension: ".otf"),
                  CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil) else {
                print("fail register font")
                return
            }
        }
    }
}

그러면 AppDelegate나

시작하는 곳에서 최초1번 폰트을 등록해주는

registerFont함수를 실행시킴으로써

모든폰트를 등록하고 정상적으로 사용할 수 있어요

 

 

 

 

참고

https://minsone.github.io/ios/mac/ios-register-custom-font

반응형