概要
アプリ内で期間限定キャンペーンや告知を表示するための、背景を半透明にし中央に画像を配置するモーダルダイアログをObjective-Cで作成します。画像タップでURLへ遷移し、✕ボタンで閉じる仕様とし、トランジションアニメーションもカスタマイズします。
1. ダイアログ本体
ヘッダファイル:CampaignDialogViewController.h
#import <UIKit/UIKit.h>
@interface CampaignDialogViewController : UIViewController <UIViewControllerTransitioningDelegate>
- (instancetype)initWithBannerImage:(UIImage *)banner
actionURL:(NSString *)url;
- (void)closeDialog;
@property (copy, nonatomic) NSString *actionURL;
@end
実装ファイル:CampaignDialogViewController.m
#import "CampaignDialogViewController.h"
#import "FadeTransitionAnimator.h"
@interface CampaignDialogViewController ()
@property (strong, nonatomic) UIImageView *bannerView;
@property (strong, nonatomic) UIButton *closeBtn;
@end
@implementation CampaignDialogViewController
- (instancetype)initWithBannerImage:(UIImage *)banner actionURL:(NSString *)url {
self = [super init];
if (self) {
_actionURL = url;
// 背景
self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
// バナー画像
CGFloat ratio = banner.size.height / banner.size.width;
CGFloat w = [self scaled:300]; // 画面幅に応じたスケーリング
CGFloat h = w * ratio;
_bannerView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, w, h)];
_bannerView.image = banner;
_bannerView.contentMode = UIViewContentModeScaleAspectFill;
_bannerView.layer.cornerRadius = 8;
_bannerView.clipsToBounds = YES;
_bannerView.center = self.view.center;
[self.view addSubview:_bannerView];
// 閉じるボタン
_closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[_closeBtn setImage:[UIImage imageNamed:@"ic_close"] forState:UIControlStateNormal];
[_closeBtn addTarget:self action:@selector(closeDialog) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_closeBtn];
// Auto Layout
_closeBtn.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[_closeBtn.widthAnchor constraintEqualToConstant:[self scaled:28]],
[_closeBtn.heightAnchor constraintEqualToAnchor:_closeBtn.widthAnchor],
[_closeBtn.trailingAnchor constraintEqualToAnchor:_bannerView.trailingAnchor constant:-8],
[_closeBtn.topAnchor constraintEqualToAnchor:_bannerView.topAnchor constant:8]
]];
// タップジェスチャー
_bannerView.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleBannerTap:)];
[_bannerView addGestureRecognizer:tap];
}
return self;
}
- (CGFloat)scaled:(CGFloat)value {
return value * [UIScreen mainScreen].bounds.size.width / 375.0;
}
- (void)handleBannerTap:(UITapGestureRecognizer *)gesture {
if (self.actionURL.length) {
[self closeDialog];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.actionURL]
options:@{}
completionHandler:nil];
}
}
- (void)closeDialog {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented
presentingController:(UIViewController *)presenting
sourceController:(UIViewController *)source {
FadeTransitionAnimator *animator = [[FadeTransitionAnimator alloc] init];
animator.presenting = YES;
return animator;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
FadeTransitionAnimator *animator = [[FadeTransitionAnimator alloc] init];
animator.presenting = NO;
return animator;
}
@end
2. トランジションアニメーター
FadeTransitionAnimator.h
#import <UIKit/UIKit.h>
@interface FadeTransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) BOOL presenting;
@end
FadeTransitionAnimator.m
#import "FadeTransitionAnimator.h"
@implementation FadeTransitionAnimator
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.25;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *container = transitionContext.containerView;
if (self.presenting) {
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
toVC.view.alpha = 0;
[container addSubview:toVC.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
toVC.view.alpha = 1;
}
completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
} else {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
fromVC.view.alpha = 0;
}
completion:^(BOOL finished) {
[fromVC.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
}
@end
3. 使用例
CampaignDialogViewController *dialog = [[CampaignDialogViewController alloc]
initWithBannerImage:[UIImage imageNamed:@"summer_sale"]
actionURL:@"https://example.com/campaign"];
dialog.modalPresentationStyle = UIModalPresentationOverFullScreen;
dialog.transitioningDelegate = dialog;
[self presentViewController:dialog animated:YES completion:nil];
このコードをViewControllerの適切なタイミング(例:viewDidAppear内で初回起動判定)で呼び出すことで、ユーザーに対してキャンペーン情報を効果的に提示できます。