目標
本文的主要的目標是幫助你使用 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鏈接