ios打包
This commit is contained in:
parent
cec4f941ec
commit
ba393f5266
@ -1,81 +0,0 @@
|
||||
#if UNITY_IOS
|
||||
using UnityEditor;
|
||||
using UnityEditor.Callbacks;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
|
||||
public class FirebaseIosPodfilePostProcessor
|
||||
{
|
||||
[PostProcessBuild(100)] // 优先级设为 100,在其他后处理之后执行
|
||||
public static void OnPostProcessBuild(BuildTarget target, string pathToBuiltProject)
|
||||
{
|
||||
if (target != BuildTarget.iOS) return;
|
||||
|
||||
string podfilePath = Path.Combine(pathToBuiltProject, "Podfile");
|
||||
|
||||
// 生成 Podfile
|
||||
string podfileContent = @"source 'https://cdn.cocoapods.org/'
|
||||
|
||||
platform :ios, '15.0'
|
||||
|
||||
target 'UnityFramework' do
|
||||
pod 'Firebase/Core', '12.4.0'
|
||||
pod 'Firebase/Auth', '12.4.0'
|
||||
pod 'Firebase/Database', '12.4.0'
|
||||
pod 'Firebase/Messaging', '12.4.0'
|
||||
end
|
||||
target 'Unity-iPhone' do
|
||||
end
|
||||
use_frameworks! :linkage => :static
|
||||
";
|
||||
|
||||
File.WriteAllText(podfilePath, podfileContent);
|
||||
UnityEngine.Debug.Log("[FirebaseIosPostProcessor] Podfile 已生成: " + podfilePath);
|
||||
|
||||
// 尝试自动运行 pod install
|
||||
TryRunPodInstall(pathToBuiltProject);
|
||||
}
|
||||
|
||||
private static void TryRunPodInstall(string projectPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "pod",
|
||||
Arguments = "install",
|
||||
WorkingDirectory = projectPath,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using (Process process = Process.Start(startInfo))
|
||||
{
|
||||
string stdout = process.StandardOutput.ReadToEnd();
|
||||
string stderr = process.StandardError.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
|
||||
if (process.ExitCode == 0)
|
||||
{
|
||||
UnityEngine.Debug.Log("[FirebaseIosPostProcessor] pod install 成功");
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[FirebaseIosPostProcessor] pod install 失败:\n" + stderr);
|
||||
UnityEngine.Debug.LogWarning("[FirebaseIosPostProcessor] 请手动在 " + projectPath + " 目录下执行 pod install");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[FirebaseIosPostProcessor] 未找到 CocoaPods,请手动执行:\n" +
|
||||
" 1. 安装 CocoaPods: sudo gem install cocoapods\n" +
|
||||
" 2. cd " + projectPath + "\n" +
|
||||
" 3. pod install\n" +
|
||||
" 错误: " + e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3865f2d3232564d5eac2422f14cdf6e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -4,9 +4,6 @@ FirebaseApp iOS and Android Dependencies.
|
||||
-->
|
||||
|
||||
<dependencies>
|
||||
<remoteSwiftPackage url="https://github.com/firebase/firebase-ios-sdk.git" version="12.14.0">
|
||||
<swiftPackage name="FirebaseCore" replacesPod="Firebase/Core"/>
|
||||
</remoteSwiftPackage>
|
||||
<iosPods>
|
||||
<iosPod name="Firebase/Core" version="12.14.0" minTargetSdk="15.0">
|
||||
</iosPod>
|
||||
|
||||
@ -4,9 +4,6 @@ FirebaseAuth iOS and Android Dependencies.
|
||||
-->
|
||||
|
||||
<dependencies>
|
||||
<remoteSwiftPackage url="https://github.com/firebase/firebase-ios-sdk.git" version="12.14.0">
|
||||
<swiftPackage name="FirebaseAuth" replacesPod="Firebase/Auth"/>
|
||||
</remoteSwiftPackage>
|
||||
<iosPods>
|
||||
<iosPod name="Firebase/Auth" version="12.14.0" minTargetSdk="15.0">
|
||||
</iosPod>
|
||||
|
||||
@ -4,9 +4,6 @@ FirebaseDatabase iOS and Android Dependencies.
|
||||
-->
|
||||
|
||||
<dependencies>
|
||||
<remoteSwiftPackage url="https://github.com/firebase/firebase-ios-sdk.git" version="12.14.0">
|
||||
<swiftPackage name="FirebaseDatabase" replacesPod="Firebase/Database"/>
|
||||
</remoteSwiftPackage>
|
||||
<iosPods>
|
||||
<iosPod name="Firebase/Database" version="12.14.0" minTargetSdk="15.0">
|
||||
</iosPod>
|
||||
|
||||
190
Assets/Plugins/iOS/NativeQRScanner.mm
Normal file
190
Assets/Plugins/iOS/NativeQRScanner.mm
Normal file
@ -0,0 +1,190 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// ──── 前向声明 ────
|
||||
static void presentQRScannerWithName(NSString *goName);
|
||||
|
||||
@interface QRScannerViewController : UIViewController <AVCaptureMetadataOutputObjectsDelegate>
|
||||
@property (nonatomic, strong) AVCaptureSession *session;
|
||||
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
|
||||
@property (nonatomic, copy) NSString *gameObjectName;
|
||||
@property (nonatomic, assign) BOOL hasScanned;
|
||||
@end
|
||||
|
||||
@implementation QRScannerViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor blackColor];
|
||||
|
||||
self.session = [[AVCaptureSession alloc] init];
|
||||
|
||||
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
|
||||
if (!device) {
|
||||
[self sendResultToUnity:@"ERROR:NO_CAMERA"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
|
||||
if (!input) {
|
||||
[self sendResultToUnity:[NSString stringWithFormat:@"ERROR:CAMERA_INPUT:%@", error.localizedDescription]];
|
||||
return;
|
||||
}
|
||||
|
||||
[self.session addInput:input];
|
||||
|
||||
AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
|
||||
[self.session addOutput:output];
|
||||
[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
|
||||
output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
|
||||
|
||||
self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
|
||||
self.previewLayer.frame = self.view.bounds;
|
||||
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
|
||||
[self.view.layer addSublayer:self.previewLayer];
|
||||
|
||||
// 半透明遮罩 + 扫描框
|
||||
CGFloat boxSize = self.view.bounds.size.width * 0.65;
|
||||
CGRect boxRect = CGRectMake(
|
||||
(self.view.bounds.size.width - boxSize) / 2,
|
||||
(self.view.bounds.size.height - boxSize) / 2 - 60,
|
||||
boxSize, boxSize
|
||||
);
|
||||
|
||||
CAShapeLayer *mask = [CAShapeLayer layer];
|
||||
UIBezierPath *fullPath = [UIBezierPath bezierPathWithRect:self.view.bounds];
|
||||
[fullPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:4] bezierPathByReversingPath]];
|
||||
mask.path = fullPath.CGPath;
|
||||
mask.fillColor = [UIColor colorWithWhite:0 alpha:0.5].CGColor;
|
||||
[self.view.layer addSublayer:mask];
|
||||
|
||||
CAShapeLayer *border = [CAShapeLayer layer];
|
||||
border.path = [UIBezierPath bezierPathWithRoundedRect:boxRect cornerRadius:4].CGPath;
|
||||
border.strokeColor = [UIColor greenColor].CGColor;
|
||||
border.lineWidth = 2;
|
||||
border.fillColor = UIColor.clearColor.CGColor;
|
||||
[self.view.layer addSublayer:border];
|
||||
|
||||
UILabel *hint = [[UILabel alloc] init];
|
||||
hint.text = @"将二维码放入框内,即可自动扫描";
|
||||
hint.textColor = UIColor.whiteColor;
|
||||
hint.font = [UIFont systemFontOfSize:14];
|
||||
hint.textAlignment = NSTextAlignmentCenter;
|
||||
hint.frame = CGRectMake(20, boxRect.origin.y + boxRect.size.height + 20,
|
||||
self.view.bounds.size.width - 40, 30);
|
||||
[self.view addSubview:hint];
|
||||
|
||||
UIButton *cancel = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
[cancel setTitle:@"关闭" forState:UIControlStateNormal];
|
||||
[cancel setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
|
||||
cancel.titleLabel.font = [UIFont systemFontOfSize:17];
|
||||
cancel.frame = CGRectMake(0, self.view.bounds.size.height - 100,
|
||||
self.view.bounds.size.width, 50);
|
||||
[cancel addTarget:self action:@selector(onCancel) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.view addSubview:cancel];
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self.session startRunning];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)onCancel {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self.session stopRunning];
|
||||
});
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[self sendResultToUnity:@"CANCEL"];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)sendResultToUnity:(NSString *)result {
|
||||
const char *go = [self.gameObjectName UTF8String];
|
||||
const char *msg = [result UTF8String];
|
||||
UnitySendMessage((char *)go, "OnScanResult", (char *)msg);
|
||||
}
|
||||
|
||||
#pragma mark - AVCaptureMetadataOutputObjectsDelegate
|
||||
|
||||
- (void)captureOutput:(AVCaptureOutput *)output
|
||||
didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects
|
||||
fromConnection:(AVCaptureConnection *)connection {
|
||||
|
||||
if (self.hasScanned) return;
|
||||
if (metadataObjects.count == 0) return;
|
||||
|
||||
AVMetadataMachineReadableCodeObject *code = metadataObjects.firstObject;
|
||||
if (![code isKindOfClass:[AVMetadataMachineReadableCodeObject class]]) return;
|
||||
|
||||
self.hasScanned = YES;
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self.session stopRunning];
|
||||
});
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[self sendResultToUnity:code.stringValue ?: @"ERROR:EMPTY_RESULT"];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
[self.session stopRunning];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// ──── C 函数实现 ────
|
||||
|
||||
static void presentQRScannerWithName(NSString *goName) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIViewController *rootVC = UnityGetGLViewController();
|
||||
if (!rootVC) {
|
||||
UnitySendMessage((char *)[goName UTF8String],
|
||||
"OnScanResult",
|
||||
"ERROR:NO_ROOT_VC");
|
||||
return;
|
||||
}
|
||||
|
||||
QRScannerViewController *scanner = [[QRScannerViewController alloc] init];
|
||||
scanner.gameObjectName = goName;
|
||||
scanner.modalPresentationStyle = UIModalPresentationFullScreen;
|
||||
[rootVC presentViewController:scanner animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
void _StartScanIOS(const char *gameObjectName)
|
||||
{
|
||||
NSString *goName = [NSString stringWithUTF8String:gameObjectName];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
|
||||
|
||||
if (status == AVAuthorizationStatusNotDetermined) {
|
||||
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
|
||||
if (granted) {
|
||||
presentQRScannerWithName(goName);
|
||||
} else {
|
||||
UnitySendMessage((char *)[goName UTF8String],
|
||||
"OnScanResult",
|
||||
"ERROR:PERMISSION_DENIED");
|
||||
}
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
if (status == AVAuthorizationStatusAuthorized) {
|
||||
presentQRScannerWithName(goName);
|
||||
} else {
|
||||
UnitySendMessage((char *)[goName UTF8String],
|
||||
"OnScanResult",
|
||||
"ERROR:PERMISSION_DENIED");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
33
Assets/Plugins/iOS/NativeQRScanner.mm.meta
Normal file
33
Assets/Plugins/iOS/NativeQRScanner.mm.meta
Normal file
@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7adc8635ee20a497c8ff442ae14a56f7
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 0
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
iPhone: iOS
|
||||
second:
|
||||
enabled: 1
|
||||
settings:
|
||||
AddToEmbeddedBinaries: false
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -29,14 +29,13 @@ namespace Kill.UI.Pages
|
||||
currentActivity.Call("startActivityForResult", intent, 0);
|
||||
}
|
||||
#elif UNITY_IOS
|
||||
// iOS 需要写原生插件
|
||||
_StartScanIOS();
|
||||
_StartScanIOS(gameObject.name);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if UNITY_IOS
|
||||
[System.Runtime.InteropServices.DllImport("__Internal")]
|
||||
private static extern void _StartScanIOS();
|
||||
private static extern void _StartScanIOS(string gameObjectName);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user