고급 설정
이 페이지에서는 피드 지면에서 구현할 수 있는 기능과 각 기능의 설정을 변경하는 방법을 확인할 수 있습니다.
지면 상의 기능을 변경하거나 구현하려면 SDK에서 제공하는 기본 UI를 사용하거나 직접 클래스를 구현해야 합니다. 피드를 구성하는 주요 기능들은 아래 그림과 같습니다.
✏️ 참고
BZVFeedViewController
의 UI를 변경하려면 디자인 커스터마이징 토픽을 참고하세요.
하위 뷰 컨트롤러로 피드 연동하기
더 다양한 연동 방식을 제공하기 위해 BuzzAd iOS용 SDK는 피드 지면을 제공하는 기본 방식인 present
, push
외에도 하위 뷰 컨트롤러를 통한 연동을 지원합니다.
하위 뷰 컨트롤러로 피드 지면을 연동하려면, 상위 뷰 컨트롤러에 하위 뷰 컨트롤러를 추가하세요.
다음은 하위 뷰 컨트롤러로 피드 지면을 추가하여 연동하는 예시입니다.
- Swift
- Objective-C
import BuzzAdBenefit
final class ContainerViewController: UIViewController {
let buzzAdFeed = BZVBuzzAdFeed { builder in }
override func viewDidLoad() {
super.viewDidLoad()
displayContentViewController(buzzAdFeed.viewController)
}
private func displayContentViewController(_ contentViewController: UIViewController) {
addChild(contentViewController)
contentViewController.view.frame = view.bounds
view.addSubview(contentViewController.view)
contentViewController.didMove(toParent: self)
}
}
@import BuzzAdBenefit;
@interface ContainerViewController ()
@property (nonatomic, strong, readonly) BZVBuzzAdFeed *buzzAdFeed;
@end
@implementation ContainerViewController
- (void)viewDidLoad {
[super viewDidLoad];
_buzzAdFeed = [BZVBuzzAdFeed feedWithBlock:^(BZVBuzzAdFeedBuilder * _Nonnull builder) {}];
[self displayContentViewController:[_buzzAdFeed viewController]];
}
- (void)displayContentViewController:(UIViewController*)contentViewController {
[self addChildViewController:contentViewController];
contentViewController.view.frame = self.view.bounds;
[self.view addSubview:contentViewController.view];
[contentViewController didMoveToParentViewController:self];
}
@end
광고 분류 기능
피드 광고 분류 기능은 탭과 필터 UI를 사용하여 유형 별로 분류된 광고를 사용자에게 보여주는 기능입니다. 탭과 필터 기능을 사용하면 사용자가 참여하고 싶은 유형의 광고만 볼 수 있어 사용자의 광고 참여 경험과 광고 효율을 높일 수 있습니다.
광고 분류 기능에 대한 설정은 앱에서 코드를 수정하거나 재배포하지 않고 변경할 수 있습니다. 설정을 변경하려면 버즈빌 담당자에게 연락하세요.
광고 분류 탭과 필터
탭의 갯수, 탭의 이름, 탭으로 모아서 게재할 광고 유형을 설정할 수 있습니다. 탭은 최대 3개까지 설정할 수 있으며, 초기에는 광고 적립, 쇼핑 적립 탭이 설정됩니다. 이 중에서 광고 적립 탭에는 쇼핑 광고를 제외한 나머지 유형의 광고가 노출되며, 쇼핑 적립 탭에는 쇼핑 광고가 노출됩니다.
탭으로 분류할 수 있는 광고의 유형은 노출형 광고, 참여형 광고, 비디오 광고, 소셜 미디어 광고, 쇼핑 광고입니다. 각 탭은 한 개 이상의 광고 유형으로 구성할 수 있습니다. 단, 쇼핑 광고의 경우 다른 광고 유형과 함께 탭으로 구성할 수 없습니다.
탭이 여러 개의 광고 유형으로 구성되면 ‘클릭하기’, ‘참여하기’ 등 광고 유형을 세분화하는 필터 UI가 노출됩니다.
무한 스크롤 기능
피드 무한 스크롤 기능은 사용자가 피드에 진입해 할당된 광고 목록을 끝까지 스크롤 해 더 이상 볼 수 있는 광고가 없으면 자동으로 추가 광고를 할당해 기능입니다. 이 기능은 기본적으로 활성화되어 있으며, 앱에서 코드를 수정하거나 재배포를 하지 않고 무한 스크롤 기능의 활성화 여부를 설정할 수 있습니다. 설정을 변경하려면 버즈빌 담당자에게 연락하세요.
앱 UI에 진입 경로 추가
앱 화면의 아이콘, 버튼, 배너 등 레이아웃 UI에 뷰를 직접 구현하여 피드로 진입하는 경로를 만들 수 있습니다.
진입 경로를 생성할 수 있는 UI 컴포넌트의 예시는 아래 다이어그램을 참고하세요. 또한 샘플 앱에서 제공하는 다양한 커스텀 진입 경로들의 샘플 코드를 확인해 보세요.
준비 사항
- 피드 지면 기본 설정 완료
⚠️ 주의
진입 경로의 정상적인 동작을 위해서는 피드 지면 초기화하기 토픽의 설명에 따라 반드시BZVConfig
에BZVFeedConfig
를 추가해야 합니다.
진입 경로 추가하기
뷰 컨트롤러에 피드 진입 경로를 생성할 수 있습니다. 피드 진입 경로를 뷰 컨트롤러에 추가하려면 BZVFeedEntryView
를 상속받는 커스텀 뷰를 구현하고 하위 뷰로 추가하세요.
코드로 진입 경로 구현하기
다음은 코드로 BZVFeedEntryView
를 상속 받는 커스텀 뷰 클래스를 구현하고 뷰 컨트롤러의 하위 뷰로 추가하는 예시입니다.
BZVFeedEntryView
의 서브 클래스를 정의하여 레이아웃을 직접 구현하세요.
- Swift
- Objective-C
import BuzzAdBenefit
final class CustomFeedEntryView: BZVFeedEntryView {
let customSubview1 = UIView(frame: .zero)
let customSubview2 = UIView(frame: .zero)
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private func setupView() {
// subview 초기화 및 LayoutConstraint 설정
...생략...
// 클릭이 가능한 영역을 지정합니다.
clickableViews = [customSubview1, customSubview2]
}
}
@import BuzzAdBenefit;
@interface CustomFeedEntryView: BZVFeedEntryView
@end
@interface CustomFeedEntryView ()
@property (nonatomic, strong, readonly) UIView *customSubview1;
@property (nonatomic, strong, readonly) UIView *customSubview2;
@end
@implementation CustomFeedEntryView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
// subview 초기화 및 LayoutConstraint 설정
...생략...
// 클릭이 가능한 영역을 지정합니다.
self.clickableViews = @[self.customSubview1, self.customSubview2];
}
@end
- 이전 단계에서 생성한
BZVFeedEntryView
의 서브 클래스(CustomFeedEntryView
)를 초기화하여 사용하세요.
- Swift
- Objective-C
import UIKit
final class ViewController: UIViewController {
private lazy var customFeedEntryView: CustomFeedEntryView = {
let view = CustomFeedEntryView(frame: .zero)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customFeedEntryView)
// LayoutConstraint 설정
...
}
}
@interface ViewController ()
@property (nonatomic, strong, readonly) CustomFeedEntryView *customFeedEntryView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_customFeedEntryView = [[CustomFeedEntryView alloc] initWithFrame:CGRectZero];
[self.view addSubview:_customFeedEntryView];
// LayoutConstraint 설정
...
}
@end
사용자 반응 추적하기
진입 경로를 생성한 후 setEntryName
을 추가해 뷰의 노출 수와 사용자 클릭 수를 추적할 수 있습니다. 사용자 반응 추적 기능을 사용하려면 setEntryName
을 구성하는 내용을 버즈빌 담당자에게 전달하세요.
✅ 중요
이 기능은 BuzzAd iOS용 SDK v3.1.x부터 추가할 수 있습니다. v3.1.x 미만의 버전을 연동한 경우 이 기능을 탑재하려면 v3.1.x 이상으로 업데이트하세요.
✏️ 참고
- 사용자 반응 추적 기능을 추가하기 전 데이터 관리 설정을 위해 버즈빌 담당자에게 연락하세요.
setEntryName
으로 들어가는your_custom_entry_point_1
값으로는icon
,icon_message
,banner
등 뷰를 설명할 수 있는 내용을 권장합니다.
- Swift
- Objective-C
import UIKit
final class ViewController: UIViewController {
private lazy var customFeedEntryView: CustomFeedEntryView = {
let view = CustomFeedEntryView(frame: .zero)
// 뷰에 대한 사용자 반응을 추적합니다.
view.setEntryName("your_custom_entry_point_1")
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(customFeedEntryView)
// LayoutConstraint 설정
...
}
}
@interface ViewController ()
@property (nonatomic, strong, readonly) CustomFeedEntryView *customFeedEntryView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_customFeedEntryView = [[CustomFeedEntryView alloc] initWithFrame:CGRectZero];
// 뷰에 대한 사용자 반응을 추적합니다.
[_customFeedEntryView setEntryName:@"your_custom_entry_point_1"];
[self.view addSubview:_customFeedEntryView];
// LayoutConstraint 설정
...
}
@end
적립 가능한 포인트 표시하기
아래 그림과 같이 적립 가능한 포인트를 표시하여 아직 피드에 진입하지 않은 사용자의 참여율을 높일 수 있습니다.
적립 가능한 포인트를 사용자에게 미리 보여 주려면, 아래 순서대로 구현하세요.
- 피드 광고를 할당하기 위해
[BZVBuzzAdFeed load]
를 호출하세요. - 광고 할당이 성공하면
onSuccess
콜백 이벤트가 호출됩니다.- 피드 광고 할당 및 표시하기 토픽을 참고하세요.
onSuccess
콜백 이벤트에서buzzAdFeed.availableRewards
를 호출하면, 적립 가능한 총 포인트 금액을 조회할 수 있습니다.- 위에서 조회한 적립 가능한 총 포인트 금액 값으로 뷰를 업데이트하세요.
다음은 viewDidLoad
을 실행할 때마다 피드를 새로 고침하고 뷰를 업데이트하는 예시입니다.
✏️ 참고
사용자가 광고에 참여하여 리워드를 받거나 피드를 아래로 스크롤하여 새로운 광고를 불러올 경우, 피드 진입 뷰에 표시된 적립 가능한 포인트 값과 실제로 적립 가능한 포인트 값에 차이가 발생할 수도 있습니다.
- Swift
- Objective-C
final class ViewController: UIViewController {
override func viewDidLoad() {
...생략...
customFeedEntryView.buzzAdFeed.load {
self.updateFeedEntryView(reward: self.customFeedEntryView.buzzAdFeed.availableRewards)
} onFailure: { _ in
self.updateFeedEntryView(reward: 0)
}
}
private func updateFeedEntryView(reward: Int) {
// 적립 가능한 포인트 안내 문구 설정
}
}
@implementation ViewController
- (void)viewDidLoad {
...생략...
[_customFeedEntryView.buzzAdFeed loadOnSuccess:^{
[self updateFeedEntryWithReward:self->_customFeedEntryView.buzzAdFeed.availableRewards];
} onFailure:^(NSError * _Nonnull error) {
[self updateFeedEntryWithReward:0];
}];
}
- (void)updateFeedEntryWithReward:(NSInteger)reward {
// 적립 가능한 포인트 안내 문구 설정
}
@end
헤더 영역 자체 구현하기
피드 헤더 영역을 자유롭게 활용할 수 있습니다. 예를 들어, 피드 지면을 설명하는 공간으로 활용하거나 적립 가능한 금액을 표시할 수도 있습니다.
다음은 헤더 영역을 변경하는 예시입니다.
BZVFeedHeaderViewHolder
를 상속하는 클래스를 생성하고 Custom View를 헤더 영역에 구현합니다. 그리고 BZVFeedConfig
의 headerViewHolderClass
속성에 새로 구현한 클래스를 추가합니다.
- Swift
- Objective-C
final class CustomHeaderViewHolder: BZVFeedHeaderViewHolder {
let headerLabel = UILabel(frame: .zero)
override class func desiredHeight() -> CGFloat {
return 100.0
}
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)원"
}
}
@interface CustomFeedHeaderViewHolder : BZVFeedHeaderViewHolder
@end
@interface CustomFeedHeaderViewHolder ()
@property (nonatomic, strong, readonly) UILabel *headerLabel;
@end
@implementation CustomFeedHeaderViewHolder
+ (CGFloat)desiredHeight {
return 100;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:_headerLabel];
// LayoutConstraint 설정
...
}
- (void)availableRewardDidUpdate:(NSInteger)reward {
_headerLabel.text = [NSString stringWithFormat:@"리워드 %ld원", reward];
}
@end
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.headerViewHolderClass = CustomHeaderViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.headerViewHolderClass = [CustomFeedHeaderViewHolder class];
}];
기본 적립 포인트 알림 팝업 자체 구현하기
사용자가 광고에 참여하지 않아도 피드에 접근하면 일정 주기마다 포인트를 지급합니다. 기본 포인트 지급 알림 팝업을 직접 구현하고 디자인을 변경하여 사용자 경험을 개선할 수 있습니다.
✅ 중요
이 기능은 BuzzAd iOS용 SDK v3.2.x부터 추가되었습니다. v3.2.x 미만의 버전을 연동한 경우 이 기능을 탑재하려면 v3.2.x 이상으로 업데이트하세요.
다음은 기본 적립 포인트 지급 알림 팝업을 직접 구현하는 예시입니다.
BZVFeedBaseRewardViewHolder
를 상속하는 클래스를 생성하세요.
- Swift
- Objective-C
final class CustomFeedBaseRewardViewHolder: BZVFeedBaseRewardViewHolder {
let baseRewardLabel = UILabel(frame: .zero)
override class func desiredHeight() -> CGFloat {
return 100.0
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
func setUpView() {
addSubview(baseRewardLabel)
// LayoutConstraint 설정
...
}
override func didUpdateBaseReward(_ baseReward: Int) {
baseRewardLabel.text = "Received \(baseReward) points"
}
}
@interface CustomFeedBaseRewardViewHolder : BZVFeedBaseRewardViewHolder
@end
@interface CustomFeedBaseRewardViewHolder ()
@property (nonatomic, strong, readonly) UILabel *baseRewardLabel;
@end
@implementation CustomFeedBaseRewardViewHolder
+ (CGFloat)desiredHeight {
return 100;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_baseRewardLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addSubview:_baseRewardLabel];
// LayoutConstraint 설정
...
}
- (void)didUpdateBaseReward:(NSInteger)baseReward {
_baseRewardLabel.text = [NSString stringWithFormat:@"Received %@ points", @(baseReward)];
}
@end
- 이전 단계에서 생성한
CustomFeedBaseRewardViewHolder
를BZVFeedConfig
에 설정하세요.
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.baseRewardViewHolderClass = CustomFeedBaseRewardViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.baseRewardViewHolderClass = [CustomFeedBaseRewardViewHolder class];
}];
광고 디자인 자체 구현하기
BuzzAd iOS용 SDK에서 제공하는 광고는 일반 광고와 쇼핑 적립 광고가 있습니다.
- 일반 광고: 쇼핑 광고를 제외한 나머지 유형의 광고입니다.
- 쇼핑 적립 광고: 사용자가 광고를 통해 물건 구매를 달성하는 경우 사용자에게 구매 금액의 일정 비율을 포인트로 돌려주는 광고입니다.
두 유형 모두 광고를 표현할 수 있는 다양한 정보로 구성됩니다. 일반 광고와 쇼핑 적립 광고를 구성하는 요소와 UI에 대해서는 아래의 표를 참고하세요.
광고 유형 | 구성 요소 | 설명 |
---|---|---|
공통(일반, 쇼핑) | 광고 제목 (필수) | 광고의 제목입니다. 최대 10자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. |
광고 소재 (필수) |
| |
광고 설명 (필수) | 광고에 대한 상세 설명입니다. 최대 40자까지 권장하며, 필요에 따라 글자 수에 상관 없이 일정 부분은 생략 부호로 대체할 수 있습니다. | |
광고주 아이콘 (필수) |
| |
CTA 버튼 (필수) |
✏️ 참고 CTA 버튼의 디자인을 변경하려면 CTA 버튼 자체 구현하기 토픽을 참고하세요. | |
광고 알림 문구 (권장) | 광고임을 명시하는 문구입니다. (예: 광고 , ad , 스폰서 , Sponsored ) | |
쇼핑 적립 | OriginalPrice View (권장) | 상품의 원가를 표시합니다. |
Price View (권장) | 상품의 할인된 가격을 표시합니다. | |
DiscountRate View (권장) | 상품 가격의 할인율을 표시합니다. 할인율은 원가와 할인가로 비교하여 산출해서 표시해야 합니다. |
일반 광고 디자인 자체 구현하기
일반 광고의 디자인을 변경하려면 다음의 절차를 따르세요.
- 일반 광고용
BZVFeedAdViewHolder
의 상속 클래스를 구현하세요.
- 하위 뷰로
BZVNativeAdView
,BZVMediaView
,BZVDefaultCtaView
클래스를 추가하세요. renderAd
에서BZVNativeAdViewBinder
를 이용하여 광고 데이터(NativeAd
)를 앞서 생성한CustomFeedAdViewHolder
에 바인딩하세요.
- Swift
- Objective-C
import UIKit
import BuzzAdBenefit
final class CustomFeedAdViewHolder: BZVFeedAdViewHolder {
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
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpView()
}
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)
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomFeedAdViewHolder : BZVFeedAdViewHolder
@end
@interface CustomFeedAdViewHolder ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedAdViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_nativeAdView = [[BZVNativeAdView alloc] initWithFrame:CGRectZero];
[self addSubview:_nativeAdView];
_mediaView = [[BZVMediaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_mediaView];
_iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_iconImageView];
_titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_titleLabel];
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_descriptionLabel];
_ctaView = [[BZVDefaultCtaView alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_ctaView];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.iconImageView = self.iconImageView;
builder.titleLabel = self.titleLabel;
builder.descriptionLabel = self.descriptionLabel;
builder.ctaView = self.ctaView;
}];
// LayoutConstraint 설정
...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
}
@end
BZVNativeAdEventDelegate
을 통해 광고 콜백 이벤트 수신 기능을 부가적으로 추가할 수도 있습니다.
- Swift
- Objective-C
import UIKit
import BuzzAdBenefit
final class CustomFeedAdViewHolder: BZVFeedAdViewHolder, 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) {
}
}
// 부가 기능: 광고 콜백 이벤트 수신 기능을 추가합니다.
@interface CustomFeedAdViewHolder () <BZVNativeAdEventDelegate>
@end
@implementation CustomFeedAdViewHolder
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
ad.delegate = self;
}
#pragma mark - BZVNativeAdEventDelegate
- (void)didImpressAd:(BZVNativeAd *)nativeAd {
}
- (void)didClickAd:(BZVNativeAd *)nativeAd {
}
- (void)didRequestRewardForAd:(BZVNativeAd *)nativeAd {
}
- (void)didRewardForAd:(BZVNativeAd *)nativeAd withResult:(BZVRewardResult)result {
}
- (void)didParticipateAd:(BZVNativeAd *)nativeAd {
}
@end
BZVFeedConfig
에 위에서 작성한CustomFeedAdViewHolder
를 광고 뷰 홀더로 설정하세요.
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.adViewHolderClass = CustomFeedAdViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.adViewHolderClass = [CustomFeedAdViewHolder class];
}];
쇼핑 적립 광고 디자인 자체 구현하기
쇼핑 적립 광고의 디자인을 변경하려면 다음의 절차를 따르세요.
- 일반 광고 디자인 자체 구현하기 토픽을 참고해
BZVFeedAdViewHolder
의 상속 클래스를 구현하세요.
- 일반 광고용 레이아웃에서
priceLabel
,originalPriceLabel
,discountPercentageLabel
을 추가해야 합니다.
- Swift
- Objective-C
import UIKit
import BuzzAdBenefit
final class CustomFeedCpsAdViewHolder: BZVFeedAdViewHolder {
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,
]
}
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)
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomFeedCpsAdViewHolder : BZVFeedAdViewHolder
@end
@interface CustomFeedCpsAdViewHolder ()
@property (nonatomic, strong, readonly) BZVNativeAdView *nativeAdView;
@property (nonatomic, strong, readonly) BZVMediaView *mediaView;
@property (nonatomic, strong, readonly) UIImageView *iconImageView;
@property (nonatomic, strong, readonly) UILabel *titleLabel;
@property (nonatomic, strong, readonly) UILabel *descriptionLabel;
@property (nonatomic, strong, readonly) BZVDefaultCtaView *ctaView;
@property (nonatomic, strong, readonly) UILabel *priceLabel;
@property (nonatomic, strong, readonly) UILabel *originalPriceLabel;
@property (nonatomic, strong, readonly) UILabel *discountRateLabel;
@property (nonatomic, strong, readonly) BZVNativeAdViewBinder *viewBinder;
@end
@implementation CustomFeedCpsAdViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
// 광고 레이아웃 컴포넌트를 생성합니다.
...생략...
_priceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_priceLabel];
_originalPriceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_originalPriceLabel];
_discountRateLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[_nativeAdView addSubview:_discountRateLabel];
_viewBinder = [BZVNativeAdViewBinder viewBinderWithBlock:^(BZVNativeAdViewBinderBuilder * _Nonnull builder) {
builder.nativeAdView = self.nativeAdView;
builder.mediaView = self.mediaView;
builder.titleLabel = self.titleLabel;
builder.ctaView = self.ctaView;
// 부가 기능: 뷰를 클릭할 수 있도록 설정합니다.
builder.clickableViews = @[
self.mediaView,
self.priceLabel,
self.originalPriceLabel,
self.discountRateLabel,
self.ctaView,
];
}];
// LayoutConstraint 설정
...생략...
}
- (void)renderAd:(BZVNativeAd *)ad {
[_viewBinder bindWithNativeAd:ad];
BZVNativeAdProduct *product = ad.product;
if (product.discountedPrice != 0) {
// 할인이 있는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.discountedPrice)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
CGFloat discountRate = (1 - product.discountedPrice / product.price) * 100;
_discountRateLabel.text = [NSString stringWithFormat:@"%.f%%", discountRate];
} else {
// 할인이 없는 쇼핑 광고
_priceLabel.text = [self formattedStringFromNumber:@(product.price)];
_originalPriceLabel.text = [self formattedStringFromNumber:@(product.price)];
_discountRateLabel.hidden = YES;
}
}
- (NSString *)formattedStringFromNumber:(NSNumber *)number {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
return [formatter stringFromNumber:number];
}
@end
- 이전 단계에서 작성한
CustomFeedAdViewHolder
를BZVFeedConfig
에 쇼핑 광고 뷰 홀더로 설정하세요.
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.cpsAdViewHolderClass = CustomCpsAdViewHolder.self
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.cpsAdViewHolderClass = [CustomCpsAdViewHolder class];
}];
CTA 버튼 자체 구현하기
BuzzAd iOS용 SDK에서 기본으로 제공하는 UI를 사용하지 않고 CTA 버튼을 자체적으로 구현할 수 있습니다.
CTA 버튼을 직접 구현하려면 CtaViewProtocol
프로토콜을 준수하는 UIView
클래스의 서브클래스를 생성하세요.
다음은 UIStackView
를 활용하여 CTA View 클래스를 생성한 예시입니다.
- Swift
- Objective-C
import UIKit
import BuzzAdBenefit
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"
}
}
@import UIKit;
@import BuzzAdBenefit;
@interface CustomCtaView : UIStackView <BZVCtaViewProtocol>
@end
@interface CustomCtaView ()
@property (nonatomic, strong, readonly) UIImageView *rewardImageView;
@property (nonatomic, strong, readonly) UILabel *rewardLabel;
@property (nonatomic, strong, readonly) UILabel *ctaLabel;
@end
@implementation CustomCtaView
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
_rewardImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardImageView];
_rewardLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_rewardLabel];
_ctaLabel = [[UILabel alloc] initWithFrame:CGRectZero];
[self addArrangedSubview:_ctaLabel];
}
#pragma mark - BZVCtaViewProtocol
- (void)renderRewardNotAvailableViewStateWithCtaText:(NSString *)ctaText {
// 리워드가 없는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:ctaText];
}
- (void)renderRewardAvailableViewStateWithCtaText:(NSString *)ctaText reward:(NSInteger)reward {
// 리워드가 있는 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:NO];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_REWARD_IMAGE"]];
[_rewardLabel setText:[NSString stringWithFormat:@"+%ld", reward]];
[_ctaLabel setText:ctaText];
}
- (void)renderParticipatingViewStateWithCtaText:(NSString *)ctaText {
// 참여 확인 중인 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:YES];
[_rewardLabel setHidden:YES];
[_ctaLabel setText:@"YOUR_PARTICIPATING_TEXT"];
}
- (void)renderParticipatedViewStateWithCtaText:(NSString *)ctaText {
// 참여 완료한 광고에 대한 CTA 뷰 레이아웃을 정의합니다.
[_rewardImageView setHidden:NO];
[_rewardLabel setHidden:YES];
[_rewardImageView setImage:[UIImage imageNamed:@"YOUR_PARTICIPATED_IMAGE"]];
[_ctaLabel setText:@"YOUR_PARTICIPATED_TEXT"];
}
@end
광고 미할당 안내 디자인 자체 구현하기
피드 지면에 진입한 시점에 노출할 광고가 없다면 광고 미할당 안내 UI가 표시됩니다. 미할당 안내 디자인은 자체 구현하여 변경할 수 있습니다.
다음은 미할당 안내 디자인을 변경하는 예시입니다.
BZVFeedErrorViewHolder
를 상속하는 클래스를 생성합니다. 그리고 BZVFeedConfig
의 errorViewHolderClass
속성에 새로 구현한 클래스를 추가합니다. 선택적으로 updateViewWithError
메소드를 오버라이드하여 에러에 따라 UI를 업데이트 할 수 있습니다.
- Swift
- Objective-C
import UIKit
import BuzzAdBenefit
final class CustomFeedErrorViewHolder: BZVFeedErrorViewHolder {
let imageView = UIImageView(image: UIImage(named: "YOUR_ERROR_IMAGE_NAME"))
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
private func setupView() {
addSubview(imageView)
// LayoutConstraint 설정
...
}
override func updateViewWithError(_ error: Error) {
// error에 따라 UI를 업데이트 할 수 있는 코드 작성
}
}
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.errorViewHolderClass = CustomFeedErrorViewHolder.self
}
@import BuzzAdBenefit;
@interface CustomFeedErrorViewHolder : BZVFeedErrorViewHolder
@end
@interface CustomFeedErrorViewHolder ()
@property (nonatomic, strong, readonly) UIImageView *imageView;
@end
@implementation CustomFeedErrorViewHolder
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self setupView];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupView];
}
return self;
}
- (void)setupView {
UIImage *image = [UIImage imageNamed:@"YOUR_ERROR_IMAGE_NAME"];
_imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:_imageView];
// LayoutConstraint 설정
...
}
- (void)updateViewWithError:(NSError *)error {
// error에 따라 UI를 업데이트 할 수 있는 코드 작성
}
@end
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.headerViewHolderClass = [CustomFeedErrorViewHolder class];
}];
사용자의 활동 추적 권한 획득 기능
BuzzAd iOS용 SDK는 iOS 14 이상에서 사용자의 활동 추적 권한(App Tracking Transparency)을 획득하기 위한 다양한 기능을 제공합니다. 그 중 피드 지면에서는 활동 추적 권한을 획득하기 위한 다이얼로그 및 배너를 보여줄 수 있습니다.
피드 진입 시 활동 추적 권한 허용 다이얼로그 표시하기
다이얼로그 표시 설정을 하면 iOS 14 이상의 기기에서 활동 추적 허용 권한을 허용하지 않은 사용자가 피드 지면에 진입할 때 다이얼로그가 나타납니다.
다음은 피드 진입 시 활동 추적 권한 허용 다이얼로그를 표시하는 예시입니다.
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.shouldShowAppTrackingTransparencyDialog = true
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.shouldShowAppTrackingTransparencyDialog = YES;
}];
✏️ 참고
활동 추적 권한 허용에 대한 부가 기능은 iOS 14를 위한 대응 토픽을 참고하세요.
활동 추적 권한 허용에 대한 안내 배너 추가하기
피드 상단에 더 많은 리워드 획득 기회를 안내하는 배너를 표시하여 사용자가 활동 추적 권한을 허용하도록 장려할 수 있습니다. 권한 제공을 거부한 사용자에게만 표시됩니다.
✏️ 참고
BuzzAd SDK 3.21.+ 버전까지는 피드 헤더 영역에 배너를 추가할 수 있습니다. 단, 헤더 영역을 자체 구현하는 경우에는 활동 추적 권한의 허용을 안내하는 배너가 노출되지 않습니다.BuzzAd SDK 3.23.2 버전부터는 캐러셀 배너 영역에 배너가 노출됩니다.
다음은 활동 추적 권한 허용 안내 배너를 추가하는 예시입니다.
- Swift
- Objective-C
let config = BZVFeedConfig { builder in
builder.unitId = "YOUR_FEED_UNIT_ID"
builder.shouldShowAppTrackingTransparencyGuideBanner = true
}
BZVFeedConfig *config = [BZVFeedConfig configWithBlock:^(BZVFeedConfigBuilder * _Nonnull builder) {
builder.unitId = @"YOUR_FEED_UNIT_ID";
builder.shouldShowAppTrackingTransparencyGuideBanner = YES;
}];
Info.plist
파일의 속성 값을 추가하여 배너를 클릭할 때 랜딩하는 URL을 변경할 수도 있습니다.
<key>BuzzAdAppTrackingTransparencyLandingURL</key>
<string>YOUR_ATT_DESCRIPTION_URL</string>
광고 타입 확인하기
BuzzAd Android용 SDK는 노출형, 액션형 등 다양한 광고 유형별로 차별화된 리워드 제공 등 다양한 기획을 지원하기 위해 광고 유형을 확인할 수 있는 API를 제공합니다. API에 대한 자세한 내용은 자주 하는 질문의 광고 타입 확인하기를 참고하세요.
개인정보 제3자 제공 동의 UI
BuzzAd iOS용 SDK는 사용자의 개인정보 제공 동의를 받기 위한 UI를 제공합니다.
✅ 중요
버즈빌과 개인정보 위수탁 관계이며 앱에서 자체적으로 제공하는 개인정보 처리방침 정책에 버즈빌을 수탁자로 포함하고 있다면, 여기에서 안내하는 개인정보 제3자 제공 동의 UI를 띄우지 않아도 됩니다.
기존에 제공하던 함수가 Deprecated 되었고, BuzzAd iOS v3.21.x 부터는 아래의 코드는 삭제하는것을 권장합니다. 아래 코드가 없더라도 원격 제어가 가능합니다.
BuzzAdBenefit.privacyPolicyManager
PrivacyPolicyManager.presentConsentForm()
PrivacyPolicyManager.grantConsent()
PrivacyPolicyManager.revokeConsent()
PrivacyPolicyManager.isConsentGranted()
❗️ DEPRECATED
BuzzAd iOS용 SDK v3.21.x부터는 서버에서 개인정보 제3자 제공 동의 UI 표시를 제어합니다. BuzzAd iOS용 SDK v3.21.x에서 표시 여부를 설정하려면 버즈빌 담당자에게 연락하세요.