Unity3d 調用IOS相冊
- Unity3d 調用IOS相冊
- 軟件環境
- .h/ .m文件編寫
- unity3d 調用腳本IOSAlbumCamera.cs ,開始準備用www加載本地圖片,但是好像www和UnityWebRequest,但是這個在IOS上面會報錯,具體錯誤下方貼出,所以用了萬能的IO加載
- 導出Xcode,打包真機調試。
- 1.在Build的時候[圖片] MapFileParser.sh: Permission denied
- 解決方法,打開ios的終端,運行chmod +x /Users/ccc/Desktop/Album 下午10.47.06/MapFileParser.sh
- /User後面爲MapFileParser.sh 所在的地方,根據自己的地方填寫正確。
- 2.在真機運行的時候,用www和UnityWebRequest報錯,改用IO加載圖片。報錯的原因是iOS9對應用通訊安全策略進行了升級, 已不再支持http這種不安全的協議,新特性要求App內訪問的網絡必須使用HTTPS協議。當然也可以使用Http請求,但是需要Info.plist添加文件。但是還是建議跟隨時代潮流。
- 原始工程鏈接
Unity3d 調用IOS相冊
最近在做項目的時候需要調用IOS相冊,作爲IOS小白的我也試了下,中間也遇到一些坑,最後還是弄好打包真機測試成功,雖然做出來很快,但是要真的弄清Ios和Unity3d的互通消息機制還是需要慢慢摸索的。
軟件環境
1.Unity3d 2018.3.0f2
2.Xcode10.1
.h/ .m文件編寫
在U3d Assets下面的Plugins\IOS下面創建IOSCameraController.h 和IOSCameraController.m文件。創建可以先創建文本然後更改後綴名。
IOSCameraController.h文件
//import 引用頭文件 相當遠Using
#import<QuartzCore/CADisplayLink.h>
//聲明一個IOSCameraController類 繼承自UIViewController <>裏面是是協議/代理的調用聲明 可以理解爲c#的接口
@interface IOSCameraController : UIViewController<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@end
IOSCameraController.m文件
#import "IOSCameraController.h"
@implementation IOSCameraController
-(void)OpenTarget:(UIImagePickerControllerSourceType)type{
//創建UIImagePickerController實例
UIImagePickerController *picker;
picker= [[UIImagePickerController alloc]init];
//設置代理
picker.delegate = self;
//是否允許編輯 (默認爲NO)
picker.allowsEditing = YES;
//設置照片的來源
// UIImagePickerControllerSourceTypePhotoLibrary, // 來自圖庫
// UIImagePickerControllerSourceTypeCamera, // 來自相機
// UIImagePickerControllerSourceTypeSavedPhotosAlbum // 來自相冊
picker.sourceType = type;
//這裏需要判斷設備是iphone還是ipad 如果使用的是iphone並沒有問題 但是如果 是在ipad上調用相冊獲取圖片 會出現沒有確定(選擇)的按鈕 所以這裏判斷
//了一下設備,針對ipad 使用另一種方法 但是這種方法是彈出一個界面 並不是覆蓋整個界面 需要改進 試過另一種方式 重寫一個相冊界面
//(QQ的ipad選擇頭像的界面 就使用了這種方式 但是這裏我們先不講 (因爲我也不太懂 但是我按照簡書的一位老哥的文章寫出來了 這裏放一下這個簡書的鏈接
//https://www.jianshu.com/p/0ddf4f7476aa)
if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary &&[[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// 設置彈出的控制器的顯示樣式
picker.modalPresentationStyle = UIModalPresentationPopover;
//獲取這個彈出控制器
UIPopoverPresentationController *popover = picker.popoverPresentationController;
//設置代理
popover.delegate = self;
//下面兩個屬性設置彈出位置
popover.sourceRect = CGRectMake(0, 0, 0, 0);
popover.sourceView = self.view;
//設置箭頭的位置
popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
//展示選取照片控制器
[self presentViewController:picker animated:YES completion:nil];
} else {
//展示選取照片控制器
[self presentViewController:picker animated:YES completion:^{}];
}
}
//選擇完成,點擊界面中的某個圖片或者選擇(Choose)按鈕時觸發
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
//關閉界面
[picker dismissViewControllerAnimated:YES completion:^{}];
//得到照片
UIImage *image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
if (image == nil) {
image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
}
//有些時候我們拍照後經常發現導出的照片方向會有問題,要麼橫着,要麼顛倒着,需要旋轉才適合觀看。但是在ios設備上是正常的
//所以在這裏處理了圖片 讓他旋轉成我們需要的
if (image.imageOrientation != UIImageOrientationUp) {
//圖片旋轉
image = [self fixOrientation:image];
}
//獲取保存圖片的地址
NSString *imagePath = [self GetSavePath:@"Temp.jpg"];
//保存圖片到沙盒路徑 對應unity中的Application.persistentDataPath 之後我們取圖片就需要在這個路徑下取 這是一個可讀可寫的路徑
[self SaveFileToDoc:image path:imagePath];
}
//獲取保存文件的路徑 如果有返回路徑 沒有創建一個返回
-(NSString*)GetSavePath:(NSString *)filename{
NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [pathArray objectAtIndex:0];
return [docPath stringByAppendingPathComponent:filename];
}
//將圖片保存到沙盒路徑
-(void)SaveFileToDoc:(UIImage *)image path:(NSString *)path{
NSData *data;
if (UIImagePNGRepresentation(image)==nil) {
data = UIImageJPEGRepresentation(image, 1);
}else{
data = UIImagePNGRepresentation(image);
}
[data writeToFile:path atomically:YES];
//保存之後通知unity 執行對應的回調
//UnitySendMessage 是用來給unity發消息的 有三個參數 1.掛載對應回調腳本的物體名 2.回調函數的名稱 3.對應回調上的參數
UnitySendMessage("Canvas", "Message", "Temp.jpg");
}
#pragma mark 圖片處理方法
//圖片旋轉處理
- (UIImage *)fixOrientation:(UIImage *)aImage {
CGAffineTransform transform = CGAffineTransformIdentity;
switch (aImage.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (aImage.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
CGImageGetBitsPerComponent(aImage.CGImage), 0,
CGImageGetColorSpace(aImage.CGImage),
CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
switch (aImage.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
break;
}
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
@end
//由於C++編譯器需要支持函數的重載,會改變函數的名稱,因此dll的導出函數通常是標準C定義的。
//這就使得C和C++的互相調用變得很常見。但是有時可能又會直接用C來調用,不想重新寫代碼,
//讓標準C編寫的dll函數定義在C和C++編譯器下都能編譯通過,通常會使用以下的格式:(這個格式在很多成熟的代碼中很常見)
#if defined(__cplusplus)
extern "C" {
#endif
//導出接口供unity使用
void IOS_OpenCamera(){
IOSCameraController *app = [[IOSCameraController alloc]init];
UIViewController *vc = UnityGetGLViewController();
[vc.view addSubview:app.view];
[app OpenTarget:UIImagePickerControllerSourceTypeCamera];
}
void IOS_OpenAlbum(){
IOSCameraController *app = [[IOSCameraController alloc]init];
UIViewController *vc = UnityGetGLViewController();
[vc.view addSubview:app.view];
[app OpenTarget:UIImagePickerControllerSourceTypePhotoLibrary];
}
#if defined(__cplusplus)
}
#endif
unity3d 調用腳本IOSAlbumCamera.cs ,開始準備用www加載本地圖片,但是好像www和UnityWebRequest,但是這個在IOS上面會報錯,具體錯誤下方貼出,所以用了萬能的IO加載
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using UnityEngine.Networking;
using System.IO;
public class IOSAlbumCamera : MonoBehaviour
{
[SerializeField] private Button _openCamera; //打開相機按鈕
[SerializeField] private Button _openAlbum; //打開相冊按鈕
[SerializeField] private RawImage _image; //用於顯示的圖片
//引入在oc中定義的那兩個方法
[DllImport("__Internal")]
private static extern void IOS_OpenCamera();
[DllImport("__Internal")]
private static extern void IOS_OpenAlbum();
void Awake()
{
//爲兩個button添加點擊事件
_openCamera.onClick.AddListener(IOS_OpenCamera);
_openAlbum.onClick.AddListener(IOS_OpenAlbum);
}
//ios回調unity的函數
void Message(string filenName)
{
Debug.Log("<<<<<<<<<<<<<<<<<<回調>>>>>>>>>>>>>>>>>>>>>>>");
Debug.Log("fileName: " + filenName);
//我們傳進來的只是文件名字 這裏合成路徑
string filePath = Application.persistentDataPath + "/" + filenName;
Debug.Log("filePath: " + filePath);
//開啓一個協程加載圖片
double startTime = (double)Time.time;
//創建文件讀取流
FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
fileStream.Seek(0, SeekOrigin.Begin);
//創建文件長度緩衝區
byte[] bytes = new byte[fileStream.Length];
//讀取文件
fileStream.Read(bytes, 0, (int)fileStream.Length);
//釋放文件讀取流
fileStream.Close();
fileStream.Dispose();
fileStream = null;
//創建Texture
int width = 300;
int height = 372;
Texture2D texture = new Texture2D(width, height);
texture.LoadImage(bytes);
_image.texture = texture;
//StartCoroutine(HttpGetTexture(filePath));
}
IEnumerator HttpGetTexture(string url)
{
Debug.Log("HttpGetTexture1111111111111111111111");
WWW www = new WWW(url);
if (!www.isDone)
yield return www;
else
{
Debug.Log("2222222222222222222222222" );
if (www.error == null)
{
Debug.Log("3333333333333333333333333333333333333");
Texture2D texture2D = new Texture2D(128, 128);
texture2D.LoadImage(www.bytes);
_image.texture = texture2D;
}
else
Debug.LogError("Error: " + www.error);
}
}
}
導出Xcode,打包真機調試。
1.在Build的時候[圖片] MapFileParser.sh: Permission denied
[圖片]
解決方法,打開ios的終端,運行chmod +x /Users/ccc/Desktop/Album 下午10.47.06/MapFileParser.sh
/User後面爲MapFileParser.sh 所在的地方,根據自己的地方填寫正確。
2.在真機運行的時候,用www和UnityWebRequest報錯,改用IO加載圖片。報錯的原因是iOS9對應用通訊安全策略進行了升級, 已不再支持http這種不安全的協議,新特性要求App內訪問的網絡必須使用HTTPS協議。當然也可以使用Http請求,但是需要Info.plist添加文件。但是還是建議跟隨時代潮流。
原始工程鏈接
鏈接: https://download.csdn.net/download/qq_32141141/10988944.