본문으로 건너뛰기

커스터마이징

전체 테마

Buzzvil SDK에서 제공하는 기본 UI를 사용해 광고 지면이나 CTA의 색상, 아이콘 이미지 등 테마를 변경할 수 있습니다.

주요 색상

Buzzvil SDK에서 제공하는 UI 중 다이얼로그의 버튼 색상, 바텀 시트 UI의 확인 버튼 등 앱에 연동한 모든 지면 내 주요 UI의 색상(Primary color)을 변경할 수 있습니다.

Primary 색상 변경을 위해 아래와 같이 색상 리소스를 추가하세요.

✏️  참고
primary light 색상은 primary 색상에 투명도 60%를 적용한 값을 권장합니다. 예를 들어, primary 색상값이 #FF0000 이라면 primary light 색상값은 #99FF0000를 사용하세요.

import BuzzvilSDK

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...

// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
builder.primaryColor = YOUR_PRIMARY_COLOR
builder.primaryLightColor = YOUR_PRIMARY_LIGHT_COLOR
}

BuzzBenefit.shared.setGlobalTheme(theme)

BuzzBenefit.shared.initialize(with: config)
return true
}
}

리워드 아이콘

Buzzvil SDK에서 제공하는 UI 중 CTA 버튼 등의 아이콘을 변경할 수 있습니다.

다음은 BuzzBenefitThemerewardIcon를 추가해 리워드 아이콘을 변경하는 예시입니다. 각 속성 값에서 커스터마이징할 수 있는 UI에 대해서는 아래의 표를 참고하세요.

속성 값커스터마이징이 가능한 UI
rewardIcon
  • 모든 지면: CTA 뷰 리워드 아이콘
  • 베네핏허브: 유저 프로필 입력 배너 리워드 아이콘
  • 베네핏허브: 팝 옵트인 버튼 아이콘
participatedIcon
  • 모든 지면: CTA 뷰

✏️  참고
테마를 변경하려면 BuzzBenefit.initialize()을 호출해 Buzzvil SDK를 초기화하기 전에 BuzzBenefit.setGloabalTheme()을 호출해야 합니다.

import BuzzvilSDK

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
let config = ...

// BuzzBenefit.initialize()를 호출하기 전에 GlobalTheme을 설정합니다.
let theme = BuzzBenefitTheme { builder in
builder.rewardIcon = YOUR_REWARD_ICON
builder.participatedIcon = YOUR_PARTICIPATED_ICON
}
BuzzBenefit.shared.setGlobalTheme(theme)

BuzzBenefit.shared.initialize(with: config)
return true
}
}

CTA 버튼 자체 구현하기

Buzzvil SDK에서 기본으로 제공하는 UI를 사용하지 않고 CTA 버튼을 자체적으로 구현할 수 있습니다. 자체적으로 구현한 CTA 버튼을 베네핏허브와 네이티브에서 사용할 수 있습니다.

CTA 버튼을 직접 구현하려면 CtaViewProtocol 프로토콜을 준수하는 UIView 클래스의 서브클래스를 생성하세요.

다음은 UIStackView를 활용하여 CTA View 클래스를 생성한 예시입니다.

import UIKit
import BuzzvilSDK

final class CustomCtaView: UIStackView, BZVCtaViewProtocol {
let rewardImageView = UIImageView(frame: .zero)
let rewardLabel = UILabel(frame: .zero)
let ctaLabel = UILabel(frame: .zero)

required init(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}

override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}

private func setUpView() {
addArrangedSubview(rewardImageView)
addArrangedSubview(rewardLabel)
addArrangedSubview(ctaLabel)
}

// MARK: BZVCtaViewProtocol
func renderRewardNotAvailableViewState(withCtaText ctaText: String) {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = ctaText
}

func renderRewardAvailableViewState(withCtaText ctaText: String, reward: Int) {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = false

rewardImageView.image = UIImage(named: "YOUR_REWARD_IMAGE")
rewardLabel.text = "+\(reward)"
ctaLabel.text = ctaText
}

func renderParticipatingViewState(withCtaText ctaText: String) {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = true
rewardLabel.isHidden = true
ctaLabel.text = "YOUR_PARTICIPATING_TEXT"
}

func renderParticipatedViewState(withCtaText ctaText: String) {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
rewardImageView.isHidden = false
rewardLabel.isHidden = true

rewardImageView.image = UIImage(named: "YOUR_PARTICIPATED_IMAGE")
ctaLabel.text = "YOUR_PARTICIPATED_TEXT"
}
}

네이티브

네이티브 오버레이 UI

버즈빌 대시 어드민을 통해 오버레이 UI를 구성하는 이미지와 텍스트를 커스터마이징할 수 있습니다.

✏️ 참고
오버레이 UI를 커스텀하지 않거나 커스터마이징 토글을 OFF 하면 버즈빌이 제공하는 UI가 적용됩니다.


네이티브 오버레이 UI를 커스터마이징하려면 다음의 절차를 따르세요.
  1. 버즈빌 대시 어드민(https://dashboard.buzzvil.com/) 에 접속하세요.
  2. Inventory Manager에서 앱을 검색하고 선택한 후 왼쪽 메뉴 패널에서 Entry Point Contents 메뉴로 이동하세요.
  3. 진입점 유형 드롭다운 목록에서 ‘네이티브 오버레이’를 선택하세요.
  4. 오버레이 유형별로 커스터마이징 토글을 켜세요.
  5. 원하는 이미지와 UI 스트링을 등록하세요.
    • 커스터마이징 UI를 실시간으로 미리보기할 수 있습니다.
    • 오버레이 UI를 커스텀하지 않으면 버즈빌이 제공하는 UI가 적용됩니다.
    • 이미지와 UI 스트링이 지원하는 규격은 다음과 같습니다.
      • 이미지 포맷: PNG, JPG, JSON(Lottie)
      • 파일 크기: 최대 2 MB
      • 이미지 비율: 312 x 212 (가로 x 세로)
      • UI 스트링 글자 수 & 줄 수: 최대 20 자 (공백 포함) & 두 줄
  6. 오른쪽 상단의 저장하기 버튼을 클릭하세요.

✏️ 참고
SDK Configuration 에서 비활성화한 기능의 오버레이는 접힌 상태로 표시됩니다.

베네핏허브

Buzzvil SDK에서 제공하는 UI의 디자인을 변경하기 위한 방법을 안내합니다.

내비게이션 바

베네핏허브 내비게이션 바의 UI 스트링을 수정하거나 내비게이션 바 영역을 자체 구현할 수 있습니다.

내비게이션 바 UI 스트링 변경하기

내비게이션 바의 UI 스트링을 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.navigationBarTitle = "YOUR_TITLE"
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

내비게이션 바 자체 구현하기

하위 뷰 컨트롤러로 베네핏허브 연동하기 항목을 참고하여 내비게이션 바를 자체 구현할 수 있습니다.

헤더

베네핏허브 상단의 헤더를 원하는 목적에 맞게 자유롭게 활용할 수 있습니다. 예를 들어, 베네핏허브 영역을 설명하는 공간으로 활용하거나 적립 가능한 금액을 표시할 수도 있습니다.

✏️ 참고
헤더 영역을 자체 구현하지 않으면 총 적립 가능 금액을 보여주는 기본 헤더가 표시됩니다.
헤더 영역을 사용하지 않으려면 버즈빌 담당자(help@buzzvil.com)에게 연락하세요.

다음은 헤더 영역을 변경하는 예시입니다.

BZVFeedHeaderView를 상속하는 클래스를 생성하고 Custom View를 헤더 영역에 구현합니다. 그리고 BZVFeedConfigheaderViewClass 속성에 새로 구현한 클래스를 추가합니다.

import UIKit
import BuzzvilSDK

final class CustomFeedHeaderView: BZVFeedHeaderView {
let headerLabel = UILabel(frame: .zero)

override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return 60
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}

override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}

func setupView() {
addSubview(headerLabel)

// LayoutConstraint 설정
// ...
}

override func availableRewardDidUpdate(_ reward: Int) {
headerLabel.text = "리워드 \(reward)원"
}
}
let feedConfig = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.headerViewClass = CustomFeedHeaderView.self
}

광고 분류 필터

광고 분류 필터의 기본 색상은 monochrome이며, primary color를 따라가도록 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.usePrimaryColorInFilter = true
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

광고 디자인

Buzzvil SDK에서 제공하는 광고는 일반 광고와 쇼핑 광고가 있습니다.

  • 일반 광고: 쇼핑 광고를 제외한 나머지 유형의 광고입니다.
  • 쇼핑 광고: 유저가 광고를 통해 물건 구매를 달성하는 경우 유저에게 구매 금액의 일정 비율을 포인트로 돌려주는 광고입니다.

두 유형 모두 광고를 표현할 수 있는 다양한 정보로 구성됩니다. 일반 광고와 쇼핑 광고를 구성하는 요소와 UI에 대해서는 아래의 표를 참고하세요.

광고 유형구성 요소설명
공통(일반, 쇼핑)광고 제목 (필수)광고의 제목입니다. 최대 10자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다.
광고 소재 (필수)
  • 이미지, 동영상 등 광고 소재입니다. 광고 소재의 종횡비는 반드시 유지해야 합니다.
  • BZVMediaView를 사용해서 표시해야 합니다.
  • 사이즈: 1,200 x 627 (px)
  • 여백을 추가할 수 있습니다. 자세한 내용은 버즈빌 담당자에게 문의하세요.
광고 설명 (필수)광고에 대한 상세 설명입니다. 최대 40자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다.
광고주 아이콘 (필수)
  • 광고주 아이콘 이미지입니다. 반드시 아이콘의 종횡비를 유지해야 합니다.
  • 사이즈: 80 x 80 (px)
CTA 버튼 (필수)
  • 광고의 참여를 유도하는 버튼입니다. 최대 7자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다.
  • BZVDefaultCtaView 클래스를 사용해서 표시해야 합니다.

  • ✏️  참고
    CTA 버튼의 디자인을 변경하려면 CTA 버튼 자체 구현하기 토픽을 참고하세요.
클릭 가능한 영역 설정 (필수)광고의 클릭 가능한 영역을 설정하는 기능합니다.
광고 알림 문구 (권장)광고임을 명시하는 문구입니다. (예: 광고, ad, 스폰서, Sponsored)
쇼핑 적립OriginalPrice View (권장)상품의 원가를 표시합니다.
Price View (권장)상품의 할인된 가격을 표시합니다.
DiscountRate View (권장)상품 가격의 할인율을 표시합니다. 할인율은 원가와 할인가로 비교하여 산출해서 표시해야 합니다.

일반 광고 디자인 자체 구현하기

일반 광고의 디자인을 변경하려면 다음의 절차를 따르세요.

  1. 일반 광고용 BZVFeedAdView의 상속 클래스를 구현하세요.
  • 하위 뷰로 BZVNativeAdView, BZVMediaView, BZVDefaultCtaView 클래스를 추가하세요.
  • renderAd에서 BZVNativeAdViewBinder를 이용하여 광고 데이터(NativeAd)를 앞서 생성한 CustomFeedAdView에 바인딩하세요.
import UIKit
import BuzzvilSDK

final class CustomFeedAdView: BZVFeedAdView {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)

lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
builder.clickableViews = [
self.mediaView,
self.ctaView
]
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}

override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}

override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return width * 0.8
}

func setupView() {
addSubview(nativeAdView)
nativeAdView.addSubview(mediaView)
nativeAdView.addSubview(iconImageView)
nativeAdView.addSubview(titleLabel)
nativeAdView.addSubview(descriptionLabel)
nativeAdView.addSubview(ctaView)

// LayoutConstraint 설정
// ...
}

override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
}
}

BZVNativeAdEventDelegate을 통해 광고 콜백 이벤트 수신 기능을 부가적으로 추가할 수도 있습니다.

import UIKit
import BuzzvilSDK

final class CustomFeedAdView: BZVFeedAdView, BZVNativeAdEventDelegate {

override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)
ad.delegate = self
}

// MARK: BZVNativeAdEventDelegate
func didImpress(_ nativeAd: BZVNativeAd) {
}

func didClick(_ nativeAd: BZVNativeAd) {
}

func didRequestReward(for nativeAd: BZVNativeAd) {
}

func didReward(for nativeAd: BZVNativeAd, with result: BZVRewardResult) {
}

func didParticipateAd(_ nativeAd: BZVNativeAd) {
}
}
  1. BZVFeedConfig에 위에서 작성한 CustomFeedAdView를 광고 뷰 홀더로 설정하세요.
let feedConfig = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.adViewClass = CustomFeedAdView.self
}

쇼핑 적립 광고 디자인 자체 구현하기

쇼핑 적립 광고의 디자인을 변경하려면 다음의 절차를 따르세요.

  1. 일반 광고 디자인 자체 구현하기 토픽을 참고해 BZVFeedAdView의 상속 클래스를 구현하세요.
  • 일반 광고용 레이아웃에서 priceLabel, originalPriceLabel, discountPercentageLabel을 추가해야 합니다.
import UIKit
import BuzzvilSDK

final class CustomFeedCpsAdView: BZVFeedAdView {
let nativeAdView = BZVNativeAdView(frame: .zero)
let mediaView = BZVMediaView(frame: .zero)
let iconImageView = UIImageView(frame: .zero)
let titleLabel = UILabel(frame: .zero)
let descriptionLabel = UILabel(frame: .zero)
let ctaView = BZVDefaultCtaView(frame: .zero)
let priceLabel = UILabel(frame: .zero)
let originalPriceLabel = UILabel(frame: .zero)
let discountRateLabel = UILabel(frame: .zero)

lazy var viewBinder = BZVNativeAdViewBinder { builder in
builder.nativeAdView = self.nativeAdView
builder.mediaView = self.mediaView
builder.iconImageView = self.iconImageView
builder.titleLabel = self.titleLabel
builder.descriptionLabel = self.descriptionLabel
builder.ctaView = self.ctaView
builder.clickableViews = [
self.mediaView,
self.ctaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
]
}

override class func desiredHeight(_ width: CGFloat) -> CGFloat {
return 350
}

required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}

override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}

private func setUpView() {
// 광고 레이아웃 컴포넌트를 생성합니다.
// ...생략...
nativeAdView.addSubview(priceLabel)
nativeAdView.addSubview(originalPriceLabel)
nativeAdView.addSubview(discountRateLabel)

// LayoutConstraint 설정
// ...생략...
}

override func renderAd(_ ad: BZVNativeAd) {
viewBinder.bind(with: ad)

let product = ad.product
if product.discountedPrice != 0 {
// 할인이 있는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
let discountRate = (1 - product.discountedPrice / product.price) * 100
discountRateLabel.text = String(format: "%d%%", Int(discountRate))
} else {
// 할인이 없는 쇼핑 광고
priceLabel.text = NSNumber(value: product.discountedPrice).formattedString()
originalPriceLabel.text = NSNumber(value: product.price).formattedString()
discountRateLabel.text = "0%%"
}
}
}

extension NSNumber {
func formattedString() -> String? {
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
return numberFormatter.string(from: self)
}
}
  1. 이전 단계에서 작성한 CustomFeedAdViewBZVFeedConfig에 쇼핑 광고 뷰 홀더로 설정하세요.
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.cpsAdViewClass = CustomFeedCpsAdView.self
}

광고 미할당 안내 UI

광고 미할당 시 이탈을 막기 위해 유저는 3번까지 재시도하는 UX로 설계되어 있습니다. 광고 미할당 안내 UI에서는 각 재시도 회차별 UI 스트링과 이미지를 변경할 수 있습니다.

재시도 회차제목내용버튼
0회차미션을 준비 중이에요다시 미션을 불러와서 포인트를 모아보시겠어요?미션 불러오기
1회차미션을 준비 중이에요미션을 로딩 중이에요. 잠시 후 다시 시도해주세요.다시 불러오기
2회차미션을 준비 중이에요미션 준비에 시간이 걸리고 있습니다. 잠시 후 다시 시도해주세요.한번 더 불러오기
3회차(마지막)미션을 준비 중이에요전체 필터: 지금은 참여 가능한 미션이 없습니다. 잠시 후 다시 방문해주세요.
그 외 필터: 지금은 참여 가능한 미션이 없습니다. 다른 미션에 참여해서 포인트를 모아보세요!
전체 필터: 나중에 다시 올게요
그 외 필터: 다른 미션 참여하기
let feedTheme = BZVFeedTheme { builder in
builder.noFillErrorImage = ...
builder.noFillErrorTitle = ...
builder.noFillErrorDescription1st = ...
builder.noFillErrorDescription2nd = ...
builder.noFillErrorDescription3rd = ...
builder.noFillErrorDescriptionFinalAllFilter = ...
builder.noFillErrorDescriptionFinalOtherFilters = ...
builder.noFillErrorButton1st = ...
builder.noFillErrorButton2nd = ...
builder.noFillErrorButton3rd = ...
builder.noFillErrorButtonFinalAllFilter = ...
builder.noFillErrorButtonFinalOtherFilters = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

유저 프로필 오류 안내 UI

유저 프로필이 설정되지 않았을 때 유저에게 노출되는 화면의 UI 스트링과 이미지를 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.userProfileErrorImage = ...
builder.userProfileErrorTitle = ...
builder.userProfileErrorDescription = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

기타 오류 화면 UI

기타 오류 화면별 이미지를 변경할 수 있습니다.

속성설명
privacyPolicyErrorIcon개인정보 제3자 제공 동의를 받아야 하는 경우 노출되는 이미지를 변경합니다.
agePolicyErrorIcon네트워크 에러 발생 시 노출되는 이미지를 변경합니다.
networkErrorIcon네트워크 에러 발생 시 노출되는 이미지를 변경합니다.
unknownErrorIcon알 수 없는 오류 발생 시 노출되는 이미지를 변경합니다.
let feedTheme = BZVFeedTheme { builder in
builder.privacyPolicyErrorImage = ...
builder.agePolicyErrorImage = ...
builder.networkErrorImage = ...
builder.unknownErrorImage = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

ATT 허용 유도 모달

iOS 14 이상에서 앱 활동 추적 권한을 허용하지 않은 유저에게 권한 허용을 유도하는 모달의 이미지를 변경할 수 있습니다.

let feedTheme = BZVFeedTheme { builder in
builder.appTrackingTransparencyGuideModalImage = ...
}
BZVBuzzAdFeed.setDefaultTheme(feedTheme)

인터스티셜

주요 UI의 색상(Primary color)을 변경할 수 있는 GlobalTheme을 사용해 진입 경로 레이아웃의 베네핏허브 진입 유도 문구 색상, CTA 버튼 배경 색상과 리워드 아이콘이 함께 변경됩니다. 자세한 내용은 전체 테마 토픽을 참고하세요.