iOS模仿系統相機拍照你不曾注意過的細節
距離上次寫部落格竟然過了一個月了,一方面是最近專案比較忙,另一方面是實在是有點兒懈怠了,強烈譴責一下自己。其實我最近在看一些技術書籍,發現一些好的書真心對自己幫助很大,看書的過程,好多原來模糊的概念、問題,都能感覺恍然大悟。當提筆想總結成一篇文章的時候,發現網上早已經有大量的優秀文章出現,所以就不敢獻醜了。今天寫的一篇文章,是最近自己專案中用到的,不算什麼難點,只是感覺有必要記錄一下。
需求
由於我們APP整合了有道翻譯的SDK,需要將拍出來的圖片翻譯成對應的語言,但是有道的SDK目前還做的不是很完善(比如:照片傾斜的時候,返回的角度不是很對,有道的技術說下個版本可能會更新)。於是產品要求拍照頁面做成跟系統相機類似,當使用者橫屏拍攝的時候,需要客戶端自己將圖片糾正回來,倒著拍的時候亦然。
自定義相機功能就不多說了,網上有大量的優秀文章,這裡隨便從網上找了一個,需要的可以參考下
基礎知識
首先我們需要知道每一個UIImage
物件,都有一個imageOrientation
屬性,裡面儲存著方向資訊:
typedef NS_ENUM(NSInteger, UIImageOrientation) {
UIImageOrientationUp, // default orientation
UIImageOrientationDown, // 180 deg rotation
UIImageOrientationLeft, // 90 deg CCW
UIImageOrientationRight, // 90 deg CW
UIImageOrientationUpMirrored, // as above but image mirrored along other axis. horizontal flip
UIImageOrientationDownMirrored, // horizontal flip
UIImageOrientationLeftMirrored, // vertical flip
UIImageOrientationRightMirrored, // vertical flip
};
根據這個屬性資訊,我們便可以對影像進行相應的旋轉,將圖片轉到正確的方向,如何旋轉??有兩種解決方案:
第一種:給UIImage新增Category
- (UIImage *)fixOrientation {
// No-op if the orientation is already correct
if (self.imageOrientation == UIImageOrientationUp) return self;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (self.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, 0, self.size.height);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
case UIImageOrientationUp:
case UIImageOrientationUpMirrored:
break;
}
switch (self.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
transform = CGAffineTransformTranslate(transform, self.size.height, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationUp:
case UIImageOrientationDown:
case UIImageOrientationLeft:
case UIImageOrientationRight:
break;
}
// Now we draw the underlying CGImage into a new context, applying the transform
// calculated above.
CGContextRef ctx = CGBitmapContextCreate(NULL, self.size.width, self.size.height,
CGImageGetBitsPerComponent(self.CGImage), 0,
CGImageGetColorSpace(self.CGImage),
CGImageGetBitmapInfo(self.CGImage));
CGContextConcatCTM(ctx, transform);
switch (self.imageOrientation) {
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
// Grr...
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
break;
default:
CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.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;
}
第二種:利用drawInRect
方法將影像畫到畫布上
- (UIImage *)normalizedImage {
if (self.imageOrientation == UIImageOrientationUp) return self;
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:(CGRect){0, 0, self.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
通過上面兩種方式轉換之後的UIImage
物件,其imageOrientation
屬性,都會被修改成UIImageOrientationUp
,這樣將圖片傳到後臺,或者匯出相簿的時候,就不會出現照片旋轉90度的問題。
但是有時候我們希望圖片該旋轉的時候,按照我們的意願旋轉(比如橫評拍攝的時候),豎直拍攝的時候,影像正常顯示,這時候我們就不能直接用上面的方法來判斷了。仔細觀察系統相機的拍攝,我發現除了豎直拍攝以外,別的情況下拍攝,圖片都會自動旋轉,這個時候就需要我們利用iPhone手機自帶的硬體感測器對方向進行判斷,以達到我們想要的結果,這裡主要用到加速儀
加速儀(型別:CMAcceleration)
加速儀可以檢測三維空間中的加速度 ,座標對應如下:
例如:當垂直手持手機且頂部向上,Y座標上回收到 -1G的加速度,(0,-1,0),當手機頭部朝下,得到的各個座標為:(0,1,0)
主要程式碼如下:
- (void)startDeviceMotion{
if (![self.motionManager isDeviceMotionAvailable]) {return;}
[self.motionManager setDeviceMotionUpdateInterval:1.f];
[self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
double gravityX = motion.gravity.x;
double gravityY = motion.gravity.y;
if (fabs(gravityY)>=fabs(gravityX)) {
if (gravityY >= 0) {
// UIDeviceOrientationPortraitUpsideDown
[self setDeviceDirection:SSDeviceDirectionDown];
NSLog(@"頭向下");
} else {
// UIDeviceOrientationPortrait
[self setDeviceDirection:SSDeviceDirectionUp];
NSLog(@"豎屏");
}
} else {
if (gravityX >= 0) {
// UIDeviceOrientationLandscapeRight
[self setDeviceDirection:SSDeviceDirectionRight];
NSLog(@"頭向右");
} else {
// UIDeviceOrientationLandscapeLef
[self setDeviceDirection:SSDeviceDirectionLeft];
NSLog(@"頭向左");
}
}
}];
}
獲取到方向資訊,下面就可以對圖片進行對應的處理了,主要用到了下面的這個方法:
- (instancetype)initWithCIImage:(CIImage *)ciImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(6_0);
該方法的作用是:
Creates and returns an image object with the specified scale and orientation factors.
建立並返回具有指定比例和方向特徵的image物件。
最後對拍攝的圖片進行處理:
UIImage *transImage = [rsltImage fixOrientation];
switch (self.deviceDirection) {
case SSDeviceDirectionUp:
transImage = [rsltImage fixOrientation];
break;
case SSDeviceDirectionLeft:
transImage = [rsltImage fixImageByOrientation:UIImageOrientationLeft];
break;
case SSDeviceDirectionRight:
transImage = [rsltImage fixImageByOrientation:UIImageOrientationRight];
break;
case SSDeviceDirectionDown:
transImage = [rsltImage fixImageByOrientation:UIImageOrientationDown];
break;
default:
break;
}
最終效果圖
總結
功能實現起來其實並不難,當時和同事糾結的地方在於,到底是採用支援橫豎屏還是採用加速度感測器上面,最後經過分析系統相機,我還是採用了利用感測器做判斷,期間也是查閱了很多的技術文章,無意中發現了一篇真心值得仔細閱讀的關於圖片解壓縮的文章。最後,再次對最近的鬆懈進行反思,繼續擼起袖子,加油幹!!!
Refrence
https://www.cnblogs.com/sunyanyan/p/5213854.html
http://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/
相關文章
- [iOS]過渡動畫之入門模仿系統iOS動畫
- 企業建站的相關注意細節分享
- Android呼叫系統相簿和相機拍照Android
- 你需要注意的Java小細節(一)Java
- 搭建直播系統前需要注意的細節有哪些?
- Android使用者請注意,你的相機正在偷偷開啟並拍照攝像Android
- Android 呼叫系統相機拍照 . 選取本地相簿Android
- Android:呼叫系統相機實現拍照+裁切(相容7.0以上系統)Android
- 蘋果提交手機雙相機系統專利 iPhone拍照媲美單反蘋果iPhone
- Android CardView 開發過程中要注意的細節AndroidView
- iOS中 讀取相簿,呼叫系統相機iOS
- linux-HA 系統的故障切換過程細節。Linux
- Golang陣列注意細節Golang陣列
- QT小小細節注意點QT
- Android學習之呼叫系統相機拍照、截圖並儲存Android
- 你的佳能相機裝 Linux 系統了嗎?Linux
- JavaWeb—Servlet基礎(細節版,相當細節)JavaWebServlet
- iOS 8.1.3系統再曝光:修復底層Bug 完善細節iOS
- 你不曾見過的酷炫地圖視覺化作品(一)地圖視覺化
- [iOS]過渡動畫之高階模仿 airbnbiOS動畫AI
- iOS 調節系統音量iOS
- 抽獎系統細節玩法
- android7.0以上呼叫系統相機拍照並顯示到ImageView上AndroidView
- Java面試要注意哪些細節Java面試
- Java泛型應該注意的細節Java泛型
- 直播系統開發中選擇伺服器需要注意哪些細節伺服器
- iOS中使用UICollectionView的細節iOSUIView
- iOS基礎細節iOS
- iOS開發細節iOS
- 這些Java程式碼最佳化細節,你需要注意!Java
- 優良的sql語句在書寫過程中要注意的細節SQL
- JavaScript初學者應注意的七個細節JavaScript
- iOS 呼叫系統相簿拍照時顯示英文問題iOS
- 選購工業網路交換機值得注意的細節問題
- iOS呼叫系統相機、相簿裡面的文字顯示英文iOS
- 執行緒池中你不容錯過的一些細節執行緒
- 智慧眼鏡相機 眨眨眼就能拍照
- 關於雲控系統的各種細節