본문 바로가기
Swift

[swift] SF Symbol & UIImage.SymbolConfiguration에 대해서 알아보자

by Jimmy_iOS 2023. 9. 3.

UIImage(SystemName:)

지금까지는 간단하게 UIImage(systemName: "person.3.sequence.fill") 와 같은 방식으로

이미지를 만들어 왔지만, 렌더링 색상과 Configuration을 적용할 생각을 하지 않았는데요.

오늘은 시스템 이미지를 커스터마이징하여 사용하는 방법에 대해 살펴보겠습니다.

SFSymbols

SFSymbols는 애플에서 기본으로 제공하는 시스템 이미지입니다.

XCode에서는 UIImage(systemName:)을 사용하여 호출할 수 있습니다.

이미지를 선택할 때 SFSymbols 내에 다양한 렌더링 종류와 종류에 따른 색상이 제공됩니다.

 

렌더링 색상으로는

단색(Monochrome),

계층(HierarchicalColor),

팔레트(PaletteColors),

여러 가지 색상(Multicolor) 이 있습니다.

XCode에서 SymbolConfiguration을 사용해 렌더링 색상별로 구현하는 방법에 대해 살펴보겠습니다.

단색(Monochrome)

class func preferringMonochrome() -> Self

이 메서드는 시스템 이미지의 단색 모드를 사용하여 심볼 이미지가 표시되도록 지정하는 색상 구성을 만듭니다.

코드로는 다음과 같이 작성할 수 있습니다.

 

typealias SFConfig = UIImage.SymbolConfiguration
private let sfImage1 = UIImageView()

sfImage1.image = UIImage(
            systemName: "person.crop.circle.badge.plus",
            withConfiguration: SFConfig.preferringMonochrome()
                .applying(SFConfig(pointSize: 100, weight: .medium))
        )
sfImage1.tintColor = .systemOrange

applying() 메서드를 통해 SymbolConfiguration들을 추가해줄 수 있습니다.

SymbolConfiguration에 관해서는 뒷부분에서 다뤄보겠습니다.

계층(HierarchicalColor)

 

코드로는 다음과 같이 작성할 수 있습니다.

typealias SFConfig = UIImage.SymbolConfiguration
private let sfImage1 = UIImageView()

sfImage1.image = UIImage(
    systemName: "person.3.sequence.fill",
    withConfiguration: SFConfig(hierarchicalColor: .systemRed)
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

init(hierarchicalColor: UIColor)

이 메서드는 단일 색상에서 파생된 색상의 계층 조합 구성을 생성합니다.

팔레트(PaletteColors)

코드로는 다음과 같이 작성할 수 있습니다.

typealias SFConfig = UIImage.SymbolConfiguration
private let sfImage1 = UIImageView()

sfImage1.image = UIImage(
    systemName: "person.3.sequence.fill",
    withConfiguration: SFConfig(paletteColors: [.systemRed, .systemBlue, .systemOrange])
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

init(paletteColors: [UIColor])

이 메서드는 여러 가지 색상을 사용하여 색상 조합을 만듭니다.

이를 통해 다양한 색상 표현이 가능해집니다.

여러 가지 색상(Multicolor)

코드로는 다음과 같이 작성할 수 있습니다.

typealias SFConfig = UIImage.SymbolConfiguration
private let sfImage1 = UIImageView()

sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMulticolor()
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

sfImage1.tintColor = .systemOrange

class func preferringMulticolor() -> Self

이 메서드는 다중 색상을 지원하는 심볼의 경우 심볼 이미지가 다중 색상 구성을 만듭니다.

다중색상은 임의로 설정할 수 없는것 같습니다.

UIImage.SymbolConfiguration

이미지에 Configuration을 적용하는 방법은 2 가지가 있는데요

// withConfiguration
sfImage1.image = UIImage(
    systemName: String,
    withConfiguration: UIImage.Configuration?
)

// applyingSymbolConfiguration
sfImage1.image = UIImage(systemName: String)?
    .applyingSymbolConfiguration(configuration: UIImage.SymbolConfiguration)

이니셜라이저에서 withConfiguration을 사용하여 Configuration을 추가하는 것이

더 깔끔한 코드를 작성할 수 있는 것 같습니다.

1번 방법을 사용해 커스터마이징 하는 방법을 더 설명해 보겠습니다.

Initializer

우선 UIImage.SymbolConfiguration의 이니셜라이저의 종류를 살펴보겠습니다.

Creating a symbol configuration

init(pointSize:)

지정된 포인트 크기 정보를 사용하여 구성 객체를 만듭니다.

init(pointSize: weight)

지정된 포인트 크기와 무게 정보를 사용하여 구성 객체를 만듭니다.

init(pointSize: weight, scale)

지정된 포인트 크기, 무게 및 축척 정보를 사용하여 구성 객체를 만듭니다.

init(scale:)

지정된 축척 정보를 사용하여 구성 객체를 만듭니다.

init(textStyle:)

지정된 글꼴 텍스트 스타일 정보를 사용하여 구성 객체를 만듭니다.

init(textStyle: scale)

지정된 글꼴 텍스트 스타일 및 축척 정보를 사용하여 구성 객체를 만듭니다.

init(weight:)

지정된 무게 정보를 사용하여 구성 객체를 만듭니다.

init(font:)

지정된 글꼴 정보를 사용하여 구성 객체를 만듭니다.

init(font: scale)

지정된 글꼴 및 축척 정보를 사용하여 구성 객체를 만듭니다.

Creating a color configuration

init(hierarchicalColor: UIColor)

한 가지 색상에서 비롯된 색상 구성을 사용하여 색상 구성을 만듭니다.

init(paletteColors: \[UIColor\])

여러 색상으로 이루어진 팔레트에서 색상 구성을 생성합니다.

class func preferringMulticolor() -> Self

심볼 이미지가 다중 색상 변형을 사용하도록 지정하는 색상 구성을 생성합니다.

class func preferringMonochrome() -> Self

심볼 이미지가 단색 변형을 사용하도록 지정하는 색상 구성을 생성합니다.

 

위 이니셜라이저를 사용해 커스터마이징을 할 수 있는데요

 

예시코드를 보겠습니다.

PointSize VS Font

pointSize와 FontSize는 같습니다.

sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMonochrome()
        .applying(SFConfig(pointSize: 100, weight: .medium))
)
sfImage1.tintColor = .systemRed

sfImage2.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(hierarchicalColor: .systemRed)
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

sfImage3.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(paletteColors: [.systemRed, .systemBlue, .systemOrange])
        .applying(SFConfig(font: .systemFont(ofSize: 100, weight: .medium)))
)

sfImage4.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMulticolor()
        .applying(SFConfig(font: .systemFont(ofSize: 100, weight: .medium)))
)
sfImage4.tintColor = .systemRed

Scale

small은 기존 크기 보다 작아집니다.

medium은 기존 크기를 유지합니다.

large는 기존 크기보다 커집니다.

default는 medium과 같습니다.

sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMonochrome()
        .applying(SFConfig(pointSize: 100, weight: .medium, scale: .small))
)

sfImage2.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(hierarchicalColor: .systemRed)
        .applying(SFConfig(pointSize: 100, weight: .medium, scale: .medium))
)

sfImage3.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(paletteColors: [.systemRed, .systemBlue, .systemOrange])
        .applying(SFConfig(pointSize: 100, weight: .medium, scale: .large))
)

sfImage4.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMulticolor()
        .applying(SFConfig(pointSize: 100, weight: .medium, scale: .default))
)
sfImage4.tintColor = .systemRed

Weight

light → medium → bold → black 순으로 라인이 두꺼워집니다.

sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMonochrome()
        .applying(SFConfig(pointSize: 100, weight: .light))
)

sfImage2.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(hierarchicalColor: .systemRed)
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

sfImage3.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(paletteColors: [.systemRed, .systemBlue, .systemOrange])
        .applying(SFConfig(pointSize: 100, weight: .bold))
)

sfImage4.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMulticolor()
        .applying(SFConfig(pointSize: 100, weight: .black))
)
sfImage4.tintColor = .systemRed

기존에 정의한 Configure는 다음 Configure가 덮어씁니다

// pointSize가 font를 덮어씌움
sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMonochrome()
        .applying(SFConfig(font: .systemFont(ofSize: 80, weight: .black)))
        .applying(SFConfig(pointSize: 100, weight: .medium))
)

// font가 pointSize를 덮어씌움
sfImage2.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(hierarchicalColor: .systemRed)
        .applying(SFConfig(pointSize: 100, weight: .medium))
        .applying(SFConfig(font: .systemFont(ofSize: 80, weight: .black)))
)

// tintColor가 paletteColors를 덮어씌움
sfImage3.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig(paletteColors: [.systemRed, .systemBlue, .systemOrange])
        .applying(SFConfig(pointSize: 100, weight: .medium))
)
sfImage3.tintColor = .systemPurple

// tintColor가 hierarchicalColor를 덮어씌움
sfImage4.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMulticolor()
        .applying(SFConfig(pointSize: 100, weight: .medium))
        .applying(SFConfig(hierarchicalColor: .systemPurple))
)
sfImage4.tintColor = .systemRed

코드 개선

위 코드에서 configuration을 만들고 추가하는 과정이 번거롭습니다.

extension을 사용하여 코드를 개선해보았습니다.

Before

sfImage1.image = UIImage(
    systemName: "person.crop.circle.badge.plus",
    withConfiguration: SFConfig.preferringMonochrome()
        .applying(SFConfig(pointSize: 100, weight: .medium))
)
sfImage1.tintColor = .systemRed

After

sfImage1.image = UIImage(systemName: "person.crop.circle.badge.plus")?
    .renderingColor(.monochrome)
    .pointSize(100)

sfImage1.tintColor(.systemRed)

Configuration을 만드는 과정을 캡슐화하여 메서드 체이닝을 사용하여 쉽게 사용할 수 있습니다.

내구 구현 코드는 아래 깃허브 UIImage+Extension파일에서 확인할 수 있습니다.

VariableValue

iOS 16에서는 아래와 같은 가변 기호를 사용할 수 있습니다.

 

이미지의 변수 값에 따라 변하는 기호를 만들 수 있는데요

팔레트 구성과 같은 다른 렌더링 모드와 함께 사용할 수도 있습니다.

@objc private func sliderDidValueChange(_ sender: UISlider) {
    imageView.image = UIImage(
        systemName: "wifi",
        variableValue: Double(sender.value),
        configuration: UIImage.SymbolConfiguration(paletteColors: [.systemRed])
    )
}

오늘은 SF Symbol의 사용법, 코드를 줄이는 방법, 그리고 가변 기호에 대해 알아보았습니다.

UIView 객체를 생성할 때 좀 더 쉽고 직관적인 코드를 작성하는 방법은

아래 깃허브 링크에서 잘 정리되어 있으니 참고하시면 도움이 될 것 같습니다

 

 

https://github.com/Jimmy-Jung/JimmyKit

 

GitHub - Jimmy-Jung/JimmyKit: A stylish swift tool that makes it easy to create UIKit Components with various styles and configu

A stylish swift tool that makes it easy to create UIKit Components with various styles and configurations. - GitHub - Jimmy-Jung/JimmyKit: A stylish swift tool that makes it easy to create UIKit Co...

github.com