目标
本文的主要的目标是帮助你使用 Unity 创建 iOS 原生弹框。
你会得到的最终效果如下图
你想要遵循 iOS 的标准来显示弹框吗?
你想要移除额外的图形来减小你构建应用的大小吗?
你想要从 Unity 中显示原生的弹框来提高用户体验吗?
如果你有这些疑虑,那么现在你来对地方了。在这篇博客中,我将使用 Unity 创建 iOS 原生弹框。
第一步 介绍
弹框是一种小的遮挡或者提示用户做一些操作的警告信息。
在这儿,我们将创建3种类型的弹框
类型 | 行为 |
---|---|
消息 弹框 | 单一行为 |
确认 弹框 | 两种行为 |
评价我们 弹框 | 三种行为 |
现在让我们创建一些简单的弹窗吧!
第二步 在 Unity 中设置场景
创建新的 Unity 工程,然后保存场景到你的资源文件夹中。
为三个弹框创建三个按钮
第三步 创建脚本然后分配所有按钮的引用
创建一个脚本然后给它命名。我命名为 PopupView.cs
,现在让我们在代码中添加一个按钮点击的监听事件。
为每一个按钮创建一个方法并且在按钮点击事件添加引用。从 iOS 的对话行为中返回一个枚举存储消息的状态。
public enum MessageState
{
OK,
YES,
NO,
RATED,
REMIND,
DECLINED,
CLOSED
}
#region PUBLIC_VARIABLES
//在你想要评价的应用创建一个变量来保持 appID
public string appleId = "925623445";
#endregion
#region BUTTON_EVENT_LISTENER
// 会话按钮点击事件
public void OnDialogPopUp() {
NativeDialog dialog = new NativeDialog("TheAppGuruz", "Do you wants to know about TheAppGuruz");
dialog.SetUrlString("http://theappguruz.com/");
dialog.init();
}
// 评价按钮点击事件
public void OnRatePopUp()
{
NativeRateUS ratePopUp = new NativeRateUS("Like this game?", "Please rate to support future updates!");
ratePopUp.SetAppleId(appleId);
ratePopUp.InitRateUS();
}
// 消息按钮点击事件
public void OnMessagePopUp()
{
NativeMessage msg = new NativeMessage("TheAppGuruz", "Welcome To TheAppGuruz");
}
#endregion
现在让我们为原生的弹框行为注册委托事件监听者。
#region UNITY_DEFAULT_CALLBACKS
void OnEnable()
{
// 注册所有的委托事件监听者
IOSRateUsPopUp.onRateUSPopupComplete += OnRateUSPopupComplete;
IOSDialog.onDialogPopupComplete += OnDialogPopupComplete;
IOSMessage.onMessagePopupComplete += OnMessagePopupComplete;
}
void OnDisable()
{
// 注销所有的委托事件监听者
IOSRateUsPopUp.onRateUSPopupComplete -= OnRateUSPopupComplete;
IOSDialog.onDialogPopupComplete -= OnDialogPopupComplete;
IOSMessage.onMessagePopupComplete -= OnMessagePopupComplete;
}
#endregion
#region DELEGATE_EVENT_LISTENER
// 当点击评价弹框的按钮时会输出不同的状态
void OnRateUSPopupComplete(MessageState state)
{
switch (state)
{
case MessageState.RATED:
Debug.Log("Rate Button pressed");
break;
case MessageState.REMIND:
Debug.Log("Remind Button pressed");
break;
case MessageState.DECLINED:
Debug.Log("Declined Button pressed");
break;
}
}
// 当点击会话弹框的按钮时会输出不同的状态
void OnDialogPopupComplete(MessageState state)
{
switch (state)
{
case MessageState.YES:
Debug.Log("Yes button pressed");
break;
case MessageState.NO:
Debug.Log("No button pressed");
break;
}
}
// 当点击消息弹框的按钮时会输出不同的状态
void OnMessagePopupComplete(MessageState state)
{
Debug.Log("Ok button Clicked");
}
#endregion
第四步 创建脚本和 Objective-c 代码的相互作用
现在,创建一个脚本命名为 IOSNative.cs
来直接和 iOS 代码(Objective-c)进行交互。
#define DEBUG_MODE
using UnityEngine;
using System.Collections;
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
using System.Runtime.InteropServices;
#endif
public class IOSNative
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
[DllImport("__Internal")]
private static extern void _TAG_ShowRateUsPopUp(string title, string message, string rate, string remind, string declined);
[DllImport("__Internal")]
private static extern void _TAG_ShowDialog(string title, string message, string yes, string no);
[DllImport("__Internal")]
private static extern void _TAG_ShowMessage(string title, string message, string ok);
[DllImport("__Internal")]
private static extern void _TAG_RedirectToAppStoreRatingPage(string appId);
[DllImport("__Internal")]
private static extern void _TAG_RedirectToWebPage(string urlString);
[DllImport("__Internal")]
private static extern void _TAG_DismissCurrentAlert();
#endif
public static void showRateUsPopUP(string title, string message, string rate, string remind, string declined)
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_ShowRateUsPopUp(title, message, rate, remind, declined);
#endif
}
public static void showDialog(string title, string message, string yes, string no)
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_ShowDialog(title, message, yes, no);
#endif
}
public static void showMessage(string title, string message, string ok)
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_ShowMessage(title, message, ok);
#endif
}
public static void RedirectToAppStoreRatingPage(string appleId)
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_RedirectToAppStoreRatingPage(appleId);
#endif
}
public static void RedirectToWebPage(string urlString)
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_RedirectToWebPage(urlString);
#endif
}
public static void DismissCurrentAlert()
{
#if (UNITY_IPHONE && !UNITY_EDITOR) || DEBUG_MODE
_TAG_DismissCurrentAlert();
#endif
}
}
第五步 为不同的弹框创建脚本
正如我上面所提到的,我们将创建三种类型的弹框,让我们创建脚本来创建不同的弹框。
消息弹框
A) 创建 NativeMessage.cs
脚本为简单的消息弹框做一些基本设置。
public class NativeMessage
{
#region PUBLIC_FUNCTIONS
public NativeMessage(string title, string message)
{
init(title, message, "Ok");
}
public NativeMessage(string title, string message, string ok)
{
init(title, message, ok);
}
private void init(string title, string message, string ok)
{
#if UNITY_IPHONE
IOSMessage.Create(title, message, ok);
#endif
}
#endregion
}
B) 为简单消息弹框创建 IOSMessage.cs
脚本
public class IOSMessage : MonoBehaviour
{
#region DELEGATE
public delegate void OnMessagePopupComplete(MessageState state);
public static event OnMessagePopupComplete onMessagePopupComplete;
#endregion
#region DELEGATE_CALLS
private void RaiseOnMessagePopupComplete(MessageState state)
{
if (onMessagePopupComplete != null)
onMessagePopupComplete(state);
}
#endregion
#region PUBLIC_VARIABLES
public string title;
public string message;
public string ok;
#endregion
#region PUBLIC_FUNCTIONS
public static IOSMessage Create(string title, string message)
{
return Create(title, message, "Ok");
}
public static IOSMessage Create(string title, string message, string ok)
{
IOSMessage dialog;
dialog = new GameObject("IOSMessagePopUp").AddComponent<IOSMessage>();
dialog.title = title;
dialog.message = message;
dialog.ok = ok;
dialog.init();
return dialog;
}
public void init()
{
IOSNative.showMessage(title, message, ok);
}
#endregion
#region IOS_EVENT_LISTENER
public void OnPopUpCallBack(string buttonIndex)
{
RaiseOnMessagePopupComplete(MessageState.OK);
Destroy(gameObject);
}
#endregion
}
确认弹框
A) 创建 NativeDialog.cs
脚本为会话的消息弹框做一些基本设置。
public class NativeDialog
{
#region PUBLIC_VARIABLES
string title;
string message;
string yesButton;
string noButton;
public string urlString;
#endregion
#region PUBLIC_FUNCTIONS
public NativeDialog(string title, string message)
{
this.title = title;
this.message = message;
this.yesButton = "Yes";
this.noButton = "No";
}
public NativeDialog(string title, string message, string yesButtonText, string noButtonText)
{
this.title = title;
this.message = message;
this.yesButton = yesButtonText;
this.noButton = noButtonText;
}
public void SetUrlString(string urlString)
{
this.urlString = urlString;
}
public void init()
{
#if UNITY_IPHONE
IOSDialog dialog = IOSDialog.Create(title, message, yesButton, noButton);
dialog.urlString = urlString;
#endif
}
#endregion
}
B) 为会话消息弹框创建 IOSDialog.cs
脚本
public class IOSDialog : MonoBehaviour
{
#region DELEGATE
public delegate void OnDialogPopupComplete(MessageState state);
public static event OnDialogPopupComplete onDialogPopupComplete;
#endregion
#region DELEGATE_CALLS
private void RaiseOnOnDialogPopupComplete(MessageState state)
{
if (onDialogPopupComplete != null)
onDialogPopupComplete(state);
}
#endregion
#region PUBLIC_VARIABLES
public string title;
public string message;
public string yes;
public string no;
public string urlString;
#endregion
#region PUBLIC_FUNCTIONS
// Constructor
public static IOSDialog Create(string title, string message)
{
return Create(title, message, "Yes", "No");
}
public static IOSDialog Create(string title, string message, string yes, string no)
{
IOSDialog dialog;
dialog = new GameObject("IOSDialogPopUp").AddComponent<IOSDialog>();
dialog.title = title;
dialog.message = message;
dialog.yes = yes;
dialog.no = no;
dialog.init();
return dialog;
}
public void init()
{
IOSNative.showDialog(title, message, yes, no);
}
#endregion
#region IOS_EVENT_LISTENER
public void OnDialogPopUpCallBack(string buttonIndex)
{
int index = System.Convert.ToInt16(buttonIndex);
switch (index)
{
case 0:
IOSNative.RedirectToWebPage(urlString);
RaiseOnOnDialogPopupComplete(MessageState.YES);
break;
case 1:
RaiseOnOnDialogPopupComplete(MessageState.NO);
break;
}
Destroy(gameObject);
}
#endregion
}
评价我们弹框
A) 创建 NativeRateUS.cs
脚本为评价我们的弹框做一些基本设置。
public class NativeRateUS
{
#region PUBLIC_VARIABLES
public string title;
public string message;
public string yes;
public string later;
public string no;
public string appleId;
#endregion
#region PUBLIC_FUNCTIONS
// Constructor
public NativeRateUS(string title, string message)
{
this.title = title;
this.message = message;
this.yes = "Rate app";
this.later = "Later";
this.no = "No, thanks";
}
// Constructor
public NativeRateUS(string title, string message, string yes, string later, string no)
{
this.title = title;
this.message = message;
this.yes = yes;
this.later = later;
this.no = no;
}
// Set AppID to rate app
public void SetAppleId(string _appleId)
{
appleId = _appleId;
}
// Initialize rate popup
public void InitRateUS()
{
#if UNITY_IPHONE
IOSRateUsPopUp rate = IOSRateUsPopUp.Create(title, message, yes, later, no);
rate.appleId = appleId;
#endif
}
#endregion
}
B) 为评价我们弹框创建 IOSRateUsPopUp.cs
脚本
public class IOSRateUsPopUp : MonoBehaviour
{
#region DELEGATE
public delegate void OnRateUSPopupComplete(MessageState state);
public static event OnRateUSPopupComplete onRateUSPopupComplete;
#endregion
#region DELEGATE_CALLS
private void RaiseOnOnRateUSPopupComplete(MessageState state)
{
if (onRateUSPopupComplete != null)
onRateUSPopupComplete(state);
}
#endregion
#region PUBLIC_VARIABLES
public string title;
public string message;
public string rate;
public string remind;
public string declined;
public string appleId;
#endregion
#region PUBLIC_FUNCTIONS
public static IOSRateUsPopUp Create()
{
return Create("Like the Game?", "Rate US");
}
public static IOSRateUsPopUp Create(string title, string message)
{
return Create(title, message, "Rate Now", "Ask me later", "No, thanks");
}
public static IOSRateUsPopUp Create(string title, string message, string rate, string remind, string declined)
{
IOSRateUsPopUp popup = new GameObject("IOSRateUsPopUp").AddComponent<IOSRateUsPopUp>();
popup.title = title;
popup.message = message;
popup.rate = rate;
popup.remind = remind;
popup.declined = declined;
popup.init();
return popup;
}
public void init()
{
IOSNative.showRateUsPopUP(title, message, rate, remind, declined);
}
#endregion
#region IOS_EVENT_LISTENER
public void OnRatePopUpCallBack(string buttonIndex)
{
int index = System.Convert.ToInt16(buttonIndex);
switch (index)
{
case 0:
IOSNative.RedirectToAppStoreRatingPage(appleId);
RaiseOnOnRateUSPopupComplete(MessageState.RATED);
break;
case 1:
RaiseOnOnRateUSPopupComplete(MessageState.REMIND);
break;
case 2:
RaiseOnOnRateUSPopupComplete(MessageState.DECLINED);
break;
}
Destroy(gameObject);
}
#endregion
}
注意
在这些类中(每个弹框的 B 部分),我们创建了游戏物体并且我们使用游戏物体的名字来获得事件的回调。我们将在下一个部分(Objective-C 文件 UnitySendMessage())使用这些名字。
第六步 设置 iOS 文件
你完成了基本的代码!现在,让我们用 Objective-C 编码来创建弹框
这样做,创建新的 xcode 工程来创建 Objective-C 文件。如果你不了解 xcdoe 并不知道怎样使用 xcode 来创建工程,那么请看这里 使用 xcode 创建基本的工程。
不要担心现在的代码,你只需要在你的文件中拷贝然后粘贴。如果你在创建工程和文件时面临着任何问题,那么你可以从博客的底部下载源代码。只要你下载完了工程,你就可以拷贝所有的 iOS 文件到你的 unity 工程的 Plugins 文件夹中
回到 xcode,创建新的 Objective-C 文件命名为 DataConvertor 来转换数据。
DataConverter.h
#import <Foundation/Foundation.h>
@interface DataConvertor : NSObject
+ (NSString*) charToNSString: (char*)value;
+ (const char *) NSIntToChar: (NSInteger) value;
+ (const char *) NSStringToChar: (NSString *) value;
@end
DataConverter.m
#import "DataConvertor.h"
@implementation DataConvertor
+(NSString *) charToNSString:(char *)value {
if (value != NULL) {
return [NSString stringWithUTF8String: value];
} else {
return [NSString stringWithUTF8String: ""];
}
}
+(const char *)NSIntToChar:(NSInteger)value {
NSString *tmp = [NSString stringWithFormat:@"%ld", (long)value];
return [tmp UTF8String];
}
+ (const char *)NSStringToChar:(NSString *)value {
return [value UTF8String];
}
@end
创建另一个文件命名为 IOSNativePopUpsManager 来从 unity 脚本中调用,并且显示弹框。
IOSNativePopUpsManager.h
#import <Foundation/Foundation.h>
#import "DataConvertor.h"
@interface IOSNativePopUpsManager : NSObject
+ (void) unregisterAllertView;
@end
IOSNativePopUpsManager.m
#import "IOSNativePopUpsManager.h"
@implementation IOSNativePopUpsManager
static UIAlertController* _currentAllert = nil;
+ (void) unregisterAllertView {
if(_currentAllert != nil) {
_currentAllert = nil;
}
}
+(void) dismissCurrentAlert {
if(_currentAllert != nil) {
[_currentAllert dismissViewControllerAnimated:NO completion:nil];
_currentAllert = nil;
}
}
+(void) showRateUsPopUp: (NSString *) title message: (NSString*) msg b1: (NSString*) b1 b2: (NSString*) b2 b3: (NSString*) b3 {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *rateAction = [UIAlertAction actionWithTitle:b1 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSRateUsPopUp", "OnRatePopUpCallBack", [DataConvertor NSIntToChar:0]);
}];
UIAlertAction *laterAction = [UIAlertAction actionWithTitle:b2 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSRateUsPopUp", "OnRatePopUpCallBack", [DataConvertor NSIntToChar:1]);
}];
UIAlertAction *declineAction = [UIAlertAction actionWithTitle:b3 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSRateUsPopUp", "OnRatePopUpCallBack", [DataConvertor NSIntToChar:2]);
}];
[alertController addAction:rateAction];
[alertController addAction:laterAction];
[alertController addAction:declineAction];
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];
_currentAllert = alertController;
}
+ (void) showDialog: (NSString *) title message: (NSString*) msg yesTitle:(NSString*) b1 noTitle: (NSString*) b2{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *yesAction = [UIAlertAction actionWithTitle:b1 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSDialogPopUp", "OnDialogPopUpCallBack", [DataConvertor NSIntToChar:0]);
}];
UIAlertAction *noAction = [UIAlertAction actionWithTitle:b2 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSDialogPopUp", "OnDialogPopUpCallBack", [DataConvertor NSIntToChar:1]);
}];
[alertController addAction:yesAction];
[alertController addAction:noAction];
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];
_currentAllert = alertController;
}
+(void)showMessage: (NSString *) title message: (NSString*) msg okTitle:(NSString*) b1 {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:b1 style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[IOSNativePopUpsManager unregisterAllertView];
UnitySendMessage("IOSMessagePopUp", "OnPopUpCallBack", [DataConvertor NSIntToChar:0]);
}];
[alertController addAction:okAction];
[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];
_currentAllert = alertController;
}
extern "C" {
// Unity Call
void _TAG_ShowRateUsPopUp(char* title, char* message, char* b1, char* b2, char* b3) {
[IOSNativePopUpsManager showRateUsPopUp:[DataConvertor charToNSString:title] message:[DataConvertor charToNSString:message] b1:[DataConvertor charToNSString:b1] b2:[DataConvertor charToNSString:b2] b3:[DataConvertor charToNSString:b3]];
}
void _TAG_ShowDialog(char* title, char* message, char* yes, char* no) {
[IOSNativePopUpsManager showDialog:[DataConvertor charToNSString:title] message:[DataConvertor charToNSString:message] yesTitle:[DataConvertor charToNSString:yes] noTitle:[DataConvertor charToNSString:no]];
}
void _TAG_ShowMessage(char* title, char* message, char* ok) {
[IOSNativePopUpsManager showMessage:[DataConvertor charToNSString:title] message:[DataConvertor charToNSString:message] okTitle:[DataConvertor charToNSString:ok]];
}
void _TAG_DismissCurrentAlert() {
[IOSNativePopUpsManager dismissCurrentAlert];
}
}
@end
注意
在这个类中,我们使用 UnitySendMessage() 向 unity 发送一条消息,然后我们使用游戏物体的名字作为参数。必须和创建的游戏物体,特别是弹框类相匹配。
现在创建一个新的文件命名为 IOSNativeUtility 来重定向控制从应用程序到评价页面或者任何其他网页。
IOSNativeUtility.h
#import <Foundation/Foundation.h>
#import "DataConvertor.h"
#if UNITY_VERSION < 450
#include "iPhone_View.h"
#endif
@interface IOSNativeUtility : NSObject
@property (strong) UIActivityIndicatorView *spinner;
+ (id) sharedInstance;
- (void) redirectToRatigPage: (NSString *) appId;
@end
IOSNativeUtility.m
#import "IOSNativeUtility.h"
@implementation IOSNativeUtility
static IOSNativeUtility *_sharedInstance;
static NSString* templateReviewURLIOS7 = @"itms-apps://itunes.apple.com/app/idAPP_ID";
NSString *templateReviewURL = @"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
+ (id)sharedInstance {
if (_sharedInstance == nil) {
_sharedInstance = [[self alloc] init];
}
return _sharedInstance;
}
-(void) redirectToRatigPage:(NSString *)appId {
#if TARGET_IPHONE_SIMULATOR
NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
#else
NSString *reviewURL;
NSArray *vComp = [[UIDevice currentDevice].systemVersion componentsSeparatedByString:@"."];
if ([[vComp objectAtIndex:0] intValue] >= 7) {
reviewURL = [templateReviewURLIOS7 stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", appId]];
} else {
reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%@", appId]];
}
NSLog(@"redirecting to iTunes page, IOS version: %i", [[vComp objectAtIndex:0] intValue]);
NSLog(@"redirect URL: %@", reviewURL);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:reviewURL]];
#endif
}
-(void) openWebPage:(NSString *)urlString{
#if TARGET_IPHONE_SIMULATOR
NSLog(@"APPIRATER NOTE: iTunes App Store is not supported on the iOS simulator. Unable to open App Store page.");
#else
NSURL *url = [ [ NSURL alloc ] initWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
#endif
}
extern "C" {
void _TAG_RedirectToAppStoreRatingPage(char* appId) {
[[IOSNativeUtility sharedInstance] redirectToRatigPage: [DataConvertor charToNSString:appId ]];
}
void _TAG_RedirectToWebPage(char* urlString){
[[IOSNativeUtility sharedInstance] openWebPage:[DataConvertor charToNSString:urlString]];
}
}
@end
现在,从 xcode 工程目录中把所有的 Objective-C 文件拷贝到 unity 工程的 Plugins 目录中。
如果你在创建 xcode 工程或 Objective-C 文件时面临着任何问题,那么你可以从博客的底部下载源代码。只要你下载完了工程,你就可以拷贝所有的 Objective-C 文件到你的 unity 工程的 Plugins/iOS 文件夹中
我希望这篇博客对你是有帮助的。如果你对 iOS 原生弹框有任何问题或疑惑,那么请自由地在评论区发表评论。我一定会尽快回复你。有一个游戏开发的想法么?你还在等什么?现在就联系我们吧,不久你就会看到你的想法实现了。
下载完整代码
CSDN链接