2013年4月30日 星期二

前後鏡頭切換

轉自http://itouchs.blogspot.tw/2012/07/filter4cam-19_23.html


1. The iOS 5 Developer's Cookbook, 3/e
2. iphone - CATransform3D vs. CGAffineTransform? - Stack Overflow

A. 開啟 ViewController.m 檔案, 修改如下:

....
// step 1:
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    //@add
    self.applyFilter = NO;
    self.isUsingFrontCamera = NO;
....
}
....

// step 2:
//@add:建立相機 Session
- (void)establishCamera:(uint)whichCamera
{
....
    // Choose camera
    /*
    self.isUsingFrontCamera = NO;
    if ((whichCamera == kCameraFront) && [Filter4CamHelper frontCameraAvailable])
    {
        self.isUsingFrontCamera = YES;
    }
    */
    //@update
    if ((whichCamera == kCameraFront) && [Filter4CamHelper frontCameraAvailable])
    {
        self.isUsingFrontCamera = YES;
    }
    else
    {

        self.isUsingFrontCamera = NO;
    }
....
}
....

// step 3:
//@add: "鏡頭切換"
- (void)switchCameras
{
    if (![Filter4CamHelper numberOfCameras] > 1) return;
   
    self.isUsingFrontCamera = !self.isUsingFrontCamera;

    if ((self.isUsingFrontCamera == YES) && ![Filter4CamHelper frontCameraAvailable]) {
        self.isUsingFrontCamera = NO;
    }
   
    AVCaptureDevice *newDevice = self.isUsingFrontCamera ? [Filter4CamHelper frontCamera] : [Filter4CamHelper backCamera];
   
    [self.session beginConfiguration];
   
    // Remove existing inputs
    for (AVCaptureInput *input in [self.session inputs])
        [self.session removeInput:input];
   
    // Change the input
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:newDevice error:nil];

    [self.session addInput:captureInput];
   
    [self.session commitConfiguration];
}
....

編譯並執行:
預設後置鏡頭:


切換到前置鏡頭之結果:
1. 當設備垂直擺放: 物體左右相反.
    (說明: "麥斯威爾" 應該要從右往左排列)
2. 當設備水平擺放: 物體上下顛倒.
------------------------------------------------------------------------------------------

B. 有關 "前置鏡頭": 左右相反與上下顛倒之問題:
     1. 說明:
         a. 方向轉變的調整, 在 ViewController.m 檔案的
             captureOutput:didOutputSampleBuffer:fromConnection: 方法中,
             (1). 先呼叫了: orientationTransform: 方法, 對整個螢幕作方向調整;
             (2). 後來再呼叫: filterOrientationTransform: 方法, 只對套用濾鏡
                    顯示範圍作方向調整.

         b. 由於目前的問題, 是使用前置鏡頭時, 整個螢幕產生左右相反(垂直擺放)
             與上下顛倒(水平擺放)之問題, 因此要對 orientationTransform: 方法作調整.

         c. 先看看之前在 orientationTransform: 方法中, 對方向轉變的調整:
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
....
    return [sourceImage imageByApplyingTransform:affineTransform];
   
    備註: 在這邊使用了 CGAffineTransform(仿射轉換) 來處理方向的問題,
              但是如果要處理左右相反的問題, 就需要使用 CATransform3D.

         d. CGAffineTransform 與 CATransform3D 的差異:
             (1). CGAffineTransform線性的 2D 轉換, 適用於 NSViews, UIViews
                    與其它 2D 的 Core Graphics 基礎上.

             (2). CATransform3D 是架構在 Core Animation 上的三維投影轉換, 適用於
                    CALayers 上. CATransform3D 有與 OpenGLmodel view 矩陣相同的
                    內部結構, 這是因為 Core Animation 是建立在 OpenGL 的基礎上.
                    (例如: CALayers 是由 OpenGL 的 textures 包裹而成的)   

 **********************************************************

     2. 測試: "設備垂直擺放", 開啟 ViewController.m 檔案, 修改如下:
....
//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭:
a. "麥斯威爾" 從右往左排列是正確的.
b. 變成左右相反的有: TableView(Cell位置與文字), 4 個 button(排列位置與文字)
     3. 測試: 修正 TableView 與 button 的問題, 修改如下:
....

//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
            self.filterListTableView.layer.transform = transform3D;
            self.saveButton.layer.transform = transform3D;
            self.switchButton.layer.transform = transform3D;
            self.torchButton.layer.transform = transform3D;
            self.observerButton.layer.transform = transform3D;
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭:
a. TableView(Cell位置與文字)已正確顯示了.
b. 4 個 button 的文字顯示正確.
c. 4 個 button 的排列位置, 從左到右應該是: Save, Switch, Torch, Observe.
     4. 測試: 修正 button 的排列位置, 修改如下:
....
//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
            self.filterListTableView.layer.transform = transform3D;
            self.saveButton.layer.transform = transform3D;
            self.switchButton.layer.transform = transform3D;
            self.torchButton.layer.transform = transform3D;
            self.observerButton.layer.transform = transform3D;

            //@update for Button position            
            [self.saveButton setFrame:CGRectMake(241, 420, 66, 40)];
            [self.switchButton setFrame:CGRectMake(164, 420, 66, 40)];
            [self.torchButton setFrame:CGRectMake(87, 420, 66, 40)];
            [self.observerButton setFrame:CGRectMake(10, 420, 66, 40)];
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭: 4 個 button 的排列位置已正確了.

備註: 在之後的修正實作時, 會將 4 個 button 加到一個 UIView 之下,
           再調整此 UIView 的方向與位置.

2013年4月24日 星期三

合成二張圖片

+ (UIImage*)mergeImage:(UIImage*)first withImage:(UIImage*)second
{
    // get size of the first image
    CGImageRef firstImageRef = first.CGImage;
    CGFloat firstWidth = CGImageGetWidth(firstImageRef);
    CGFloat firstHeight = CGImageGetHeight(firstImageRef);
   
    // get size of the second image
    CGImageRef secondImageRef = second.CGImage;
    CGFloat secondWidth = CGImageGetWidth(secondImageRef);
    CGFloat secondHeight = CGImageGetHeight(secondImageRef);
   
    // build merged size
    CGSize mergedSize = CGSizeMake(MAX(firstWidth, secondWidth), MAX(firstHeight, secondHeight));
   
    // capture image context ref
    UIGraphicsBeginImageContext(mergedSize);
   
    //Draw images onto the context
    [first drawInRect:CGRectMake(0, 0, firstWidth, firstHeight)];
    [second drawInRect:CGRectMake(0, 0, secondWidth, secondHeight)];
   
    // assign context to new UIImage
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
   
    // end context
    UIGraphicsEndImageContext();
   
    return newImage;
}

直接存取攝影機

轉錄自http://furnacedigital.blogspot.tw/2012/11/avcapturestillimageoutput.html#more(作者超強,內有更多好文!)

 

透過 AVCaptureStillImageOutput 做靜態影像的擷取

 

在 iOS 6 SDK(iOS 5 SDK 以上)中捕捉攝影機的靜態拍攝畫面,製作類似「錄影同時拍照的效果」可以透果很多方式,像是透過 UIImagePickerController 的方式,呼叫 iOS SDK 所提供的 API 來捕捉畫面,或是透過 AVFoundation 的 方式,建立影像的 AVCaptureSession,並且設定對應的 Input 與 Output。而本篇文章所採用的方法屬於後者,我們使用 AVCaptureStillImageOutput 來當做 AVCaptureSession 的 Output 端,輸出靜態影像。

在開始之前請先替您的專案加上 AVFoundation.framework,並且在對應的類別中引用此標頭檔。替專案加入 Framework 的方法請參考Xcode 4 新增 Framework 的方法一文。


建立 AVCaptureSession
在這部份中我們要在類別的 @interface 區段中宣告一個 AVCaptureSession 型態的全域變數,方便之後的作業。
1
AVCaptureSession *myCaptureSession;

在建立 AVCaptureSession 的部份,其實並沒有太多的設定,唯一要注意的是 setSessionPreset: 這個方法函式,它決定了攝影機捕獲影像的解析度大小,你可以使用 AVCaptureSessionPreset 的關鍵字來查閱各種解析度的定義。
1
2
3
//建立 AVCaptureSession
myCaptureSession = [[AVCaptureSession alloc] init];
[myCaptureSession setSessionPreset:AVCaptureSessionPresetPhoto];


建立 AVCaptureDeviceInput
在建立好 AVCaptureSession 之後,接下來我們要對他的輸入端做設定,你可以透過下列程式碼來取得裝置上具有輸入特性的硬體裝置和他們實際的裝置名稱。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//建立 AVCaptureDeviceInput
NSArray *myDevices = [AVCaptureDevice devices];
for (AVCaptureDevice *device in myDevices) {
    if ([device position] == AVCaptureDevicePositionBack) {
        NSLog(@"後攝影機硬體名稱: %@", [device localizedName]);
    }
    if ([device position] == AVCaptureDevicePositionFront) {
        NSLog(@"前攝影機硬體名稱: %@", [device localizedName]);
    }
    if ([device hasMediaType:AVMediaTypeAudio]) {
        NSLog(@"麥克風硬體名稱: %@", [device localizedName]);
    }
}

裝置上具有輸入特性的硬體名稱

下列我們就以後置鏡頭為例,製作 AVCaptureSession 的輸入端。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//使用後置鏡頭當做輸入
NSError *error = nil;
for (AVCaptureDevice *device in myDevices) {
    if ([device position] == AVCaptureDevicePositionBack) {
        AVCaptureDeviceInput *myDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
        if (error) {
            //裝置取得失敗時的處理常式
        } else {
            [myCaptureSession addInput:myDeviceInput];
        }
    }
}


建立 AVCaptureVideoPreviewLayer
在建立 AVCaptureSession 與設定對應的 Input 之後,我們就可以透過 AVCaptureVideoPreviewLayer 來取得攝影機所捕捉的連續畫面,在 此,AVCaptureVideoPreviewLayer 所呈現的畫面是連續的,並非單張的靜態影像,當然你也可以略過設定 AVCaptureVideoPreviewLayer 的步驟,不顯示攝影機所拍攝到的畫面,這並不會有任何影響。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//建立 AVCaptureVideoPreviewLayer
AVCaptureVideoPreviewLayer *myPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:myCaptureSession];
[myPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
CGRect rect = CGRectMake(160, 180, 320, 240);
[myPreviewLayer setBounds:rect];
UIView *myView = [[UIView alloc]initWithFrame:rect];
[myView.layer addSublayer:myPreviewLayer];
[self.view addSubview:myView];
//啟用攝影機
[myCaptureSession startRunning];

在上述程式碼中,我們透過 AVCaptureSession 來建立 AVCaptureVideoPreviewLayer,並且設定它的 VideoGravity 屬性為 AVLayerVideoGravityResizeAspectFill,表示所取得的影像必須以等比例的方式縮放來填滿我們所指定的大小 (CGRect rect)。

到目前為止,我們還並未對 AVCaptureSession 做 Output 的設定,但是已經可以透過 AVCaptureVideoPreviewLayer 在畫面上看到攝影機所拍攝的影像。下面,我們將針對靜態影像的擷取來製作對應的 Output。


建立 AVCaptureStillImageOutput
要使用單張靜態影像的擷取,就可以考慮使用 AVCaptureStillImageOutput 來製作你的 Output 端,否則最好是使用 AVCaptureVideoDataOutput 來當做輸出端,這兩個不同類型的 Output 可以做的事情也不盡相同,關於 AVCaptureVideoDataOutput 可以參考透過 AVCaptureVideoDataOutput 做連續影像片段的擷取一文,來獲得更多資訊,下面我們只針對 AVCaptureStillImageOutput 做說明。

在這部份中我們要在類別的 @interface 區段中宣告一個 AVCaptureStillImageOutput 型態的全域變數,方便之後的作業。
1
AVCaptureStillImageOutput *myStillImageOutput;

接著,在根據以下程式碼對 AVCaptureStillImageOutput 做設定,並將此 Output 與 AVCaptureSession 做連接。
1
2
3
4
5
6
//建立 AVCaptureStillImageOutput
myStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *myOutputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil];
[myStillImageOutput setOutputSettings:myOutputSettings];
[myCaptureSession addOutput:myStillImageOutput];

在設定 AVCaptureStillImageOutput 上,我們將所擷取到的影像做 JPEG 的編碼以節省空間,雖然說使用 AVVideoCodec 這個關鍵字時,你可以看到 AVVideoCodecH264 的選項,但是他並不是靜態影像的編碼方式,而是使用在 Video 上的一種壓縮編碼,所以無法使用。


擷取單張靜態影像
在成功建立好 AVCaptureSession 的 Output 之後,接下來就是製作靜態影像擷取,你可以透過下列的方法函式,來與拍照的按鈕做互動,取得靜態影像。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
AVCaptureConnection *myVideoConnection = nil;
//從 AVCaptureStillImageOutput 中取得正確類型的 AVCaptureConnection
for (AVCaptureConnection *connection in myStillImageOutput.connections) {
    for (AVCaptureInputPort *port in [connection inputPorts]) {
        if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
            myVideoConnection = connection;
            break;
        }
    }
}
//擷取影像(包含拍照音效)
[myStillImageOutput captureStillImageAsynchronouslyFromConnection:myVideoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
    //完成擷取時的處理常式(Block)
    if (imageDataSampleBuffer) {
        NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
         //取得的靜態影像
         UIImage *myImage = [[UIImage alloc] initWithData:imageData];
         [self setImage:myImage];
         //取得影像資料(需要ImageIO.framework 與 CoreMedia.framework)
         CFDictionaryRef myAttachments = CMGetAttachment(imageDataSampleBuffer, kCGImagePropertyExifDictionary, NULL);
         NSLog(@"影像屬性: %@", myAttachments);
    }
}];

在上述程式碼中,我們必須先從 AVCaptureStillImageOutput 中取得正確類型的 AVCaptureConnection,接著,才利用此 AVCaptureConnection 做影像的擷取,另外,如果你希望改變擷取影像時的方向,也可以對 AVCaptureConnection 做 setVideoOrientation: 旋轉影像,或 setVideoMirrored: 鏡射影像。

另外,在擷影像時會包含拍照的音效,無法消除,這是基於 iOS SDK 安全條款的使用原則,如果你是想製作每秒擷取 N 張靜態影像的話,最好還是使用 AVCaptureVideoDataOutput 來當做輸出,避免產生連續的拍照音效。

最後,我們也可以使用 CMGetAttachment 來取得該影像的其他屬性,但是這必須使用 ImageIO 與 CoreMedia 兩個 Framework 才行。

使用 CMGetAttachment 所取的的影像屬性

調整self.view 的subview前後位置

將一個UIView顯示在最前面只需要調用其父視圖的 bringSubviewToFront()方法。
將一個UIView推送到背後只需要調用其父視圖的 sendSubviewToBack()方法。

2013年4月23日 星期二

Facebook SDK built error

總之,要在framework加入
1.FacebookSDKResources.bundle
2.FacebookSDK.framework
3.Sqlite3.0.dylib
4.Social.framework
5.AdSupport.framework
6.Accounts.framework

 
Q: dyld: Library not loaded: /System/Library/Frameworks/Social.framework/Social
A: add Social.framework
 
Q: dyld: Symbol not found: _ACFacebookAppIdKey
A:
Accounts.framework:Optional
AdSupport.framework:Optional
Social.framework:Optional
 
Q: /Users/shian/Documents/uschoolnet/FacebookSDK/tests/FBBatchRequestTests.h:17:9: 'SenTestingKit/SenTestingKit.h' file not found
A: Add SenTestingKit.framework to your project.
 
Q:
"_ACFacebookAppIdKey", referenced from:
"_ACFacebookAudienceEveryone", referenced from:
"_ACFacebookAudienceFriends", referenced from:
"_ACFacebookAudienceKey", referenced from:
"_ACFacebookAudienceOnlyMe", referenced from:
"_ACFacebookPermissionsKey", referenced from:
"_CLLocationCoordinate2DMake", referenced from:
"_OBJC_CLASS_$_ACAccountStore", referenced from:
"_OBJC_CLASS_$_ASIdentifierManager", referenced from:
"_OBJC_CLASS_$_SLComposeViewController", referenced from:
"_OBJC_CLASS_$_SLComposeViewController", referenced from:
"_OBJC_METACLASS_$_SenTestCase", referenced from:
"_OBJC_METACLASS_$_SenTestCase", referenced from:
clang: error: linker command failed with exit code 1 (use -v to see invocation)
A:
Add Social.framework
Add AdSupport.framework
Add Accounts.framework
 
Q: "_CLLocationCoordinate2DMake", referenced from:
A: Add CoreLocation.framework
 
Q:
"_OBJC_CLASS_$_SenTestCase", referenced from:
"_OBJC_METACLASS_$_SenTestCase", referenced from:
clang: error: linker command failed with exit code 1 (use -v to see invocation)
A:
http://stackoverflow.com/questions/9614313/xcode-4-3-build-error-missing-required-architecture-armv7
Select Project in project navigator
Select Test Target
Select Build Settings
Go down to Framework Search Paths and single-click it
Delete whatever is there
Enter $(SDKROOT)/Developer/Library/Frameworks $(DEVELOPER_LIBRARY_DIR)/Frameworks
If you had any custom frameworks, add those back
 
"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks"
 
Q: /Users/shian/Documents/uschoolnet/FacebookSDK/tests/FBTestBlocker.h:36:1: Multiple declarations of method 'initWithExpectedSignalCount:' found and ignored
A: remove tests
 
Q:Cannot run on the selected destination
A:
Social.framework  optional
AdSupport.framework  optional
Accounts.framework  optional
 
or Deployment Target 5.1
or Valid Architectures armv7

2013年4月19日 星期五

OpenGL ES入門的好blog

http://itouchs.blogspot.tw/search/label/OpenGL%20ES

iOS app 與webview中javascript互傳訊息的方式

//傳送指令,用webview的stringByEvaluatingJavaScriptFromString方法去傳 - (IBAction)executeJS:(id)sender { //NSString *returnValue = [webView stringByEvaluatingJavaScriptFromString:@"showJSON('{\"id\": \"1\"}')"]; NSString *returnValue = [webView stringByEvaluatingJavaScriptFromString:@"marq.appMessage('字串/n')"]; NSLog(@"--- return: %@", returnValue); } //實做webView的下列delegate去接webview傳來的url scheme去判斷為要執行的指令 - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType { NSURL *URL = [request URL]; if ([[URL scheme] isEqualToString:@"marq"]) { NSLog(@"==== parse the rest of the URL object and execute functions: %@", URL); } return YES; }

2013年4月16日 星期二

二種轉換UIViewController的方式

第一種是不需要NavigationController 是一種正反面的概念,由位於正面的viewcontroller發動,把背面的viewcontroller推出來,
 BookWebDetailViewController *bookWebDetailViewController = [[[BookWebDetailViewController alloc] initWithBook:aBook] autorelease];
[self presentModalViewController:bookWebDetailViewController animated:YES]; 
相同的,要把背面的viewcontroller推回去也是由正面的viewcontroller發動
[self dissmissModalViewController:bookWebDetailViewController animated:YES]; 
第二種是要用NavigationController 這是一種stack的概念,把detailview實作好再push出來,可以無限制的push下去,但也要一層一層pop回來

iPhone 轉換 UIView 時的動畫效果

轉錄自http://pro.ctlok.com/2010/03/iphone-uiview.html Apple 提供了流暢的動畫功能給各位開發者,差不多每一個 App 都會用到的動畫功能,無需複雜的程式碼便可以輕鬆使用到。 在這裡簡單介紹一下。 使用以下的程式碼便可以制作出動畫效果。 UIViewAnimationTransition transition = UIViewAnimationTransitionCurlUp; UIViewAnimationCurve curve = UIViewAnimationCurveEaseInOut; [UIView beginAnimations:@"anim" context:NULL]; [UIView setAnimationCurve:curve]; [UIView setAnimationTransition:transition forView:[self view] cache:YES]; [UIView setAnimationDuration:1.0f]; [UIView commitAnimations]; 現在雖然有動畫效果,但頁面還是沒有改變,例如要新增一個黑色的頁面。 以下程式碼要加到動畫程式碼之前。 UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; [view setBackgroundColor:[UIColor blackColor]]; [self.view addSubview:view]; 完整轉頁效果: UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)]; [view setBackgroundColor:[UIColor blackColor]]; [self.view addSubview:view]; UIViewAnimationTransition transition = UIViewAnimationTransitionCurlUp; UIViewAnimationCurve curve = UIViewAnimationCurveEaseInOut; [UIView beginAnimations:@"anim" context:NULL]; [UIView setAnimationCurve:curve]; [UIView setAnimationTransition:transition forView:[self view] cache:YES]; [UIView setAnimationDuration:1.0f]; [UIView commitAnimations]; [view release]; 是否很簡單? 其實 Apple 已經提供了四動晝效果和四種速度變化。 向上翻頁的效果: //Curl Up UIViewAnimationTransition transition = UIViewAnimationTransitionCurlUp; 向下翻頁的效果: //Curl Down UIViewAnimationTransition transition = UIViewAnimationTransitionCurlDown; 由左向右旋轉的效果: //Flip From Left UIViewAnimationTransition transition = UIViewAnimationTransitionFlipFromLeft; 由右向左旋轉的效果: //Flip From Right UIViewAnimationTransition transition = UIViewAnimationTransitionFlipFromRight; 開始時慢慢加速,結束前慢慢減速: UIViewAnimationCurve curve = UIViewAnimationCurveEaseInOut; 開始時慢慢加速: UIViewAnimationCurve curve = UIViewAnimationCurveEaseIn; 結束前慢慢減速: UIViewAnimationCurve curve = UIViewAnimationCurveEaseOut; 開始到尾也是均速: UIViewAnimationCurve curve = UIViewAnimationCurveLinear; 大家可以試試那一個效果最適合你的 App。

UIView的autoresizingMask的屬性

在 UIView 中有一個autoresizingMask的屬性,它對應的是一個枚舉的值(如下),屬性的意思就是自動調整子控件與父控件中間的位置,寬高。 enum { UIViewAutoresizingNone = 0, UIViewAutoresizingFlexibleLeftMargin = 1 << 0, UIViewAutoresizingFlexibleWidth = 1 << 1, UIViewAutoresizingFlexibleRightMargin = 1 << 2, UIViewAutoresizingFlexibleTopMargin = 1 << 3, UIViewAutoresizingFlexibleHeight = 1 << 4, UIViewAutoresizingFlexibleBottomMargin = 1 << 5 }; typedef NSUInteger UIViewAutoresizing; 分別解釋以上意思。 UIViewAutoresizingNone就是不自動調整。 UIViewAutoresizingFlexibleLeftMargin就是自動調整與superView左邊的距離,也就是說,與superView右邊的距離不變。 UIViewAutoresizingFlexibleRightMargin就是自動調整與superView的右邊距離,也就是說,與superView左邊的距離不變。 UIViewAutoresizingFlexibleTopMargin UIViewAutoresizingFlexibleBottomMargin UIViewAutoresizingFlexibleWidth UIViewAutoresizingFlexibleHeight 以上就不多解釋了,參照上面的。 也可以多個枚舉同時設置。如下: subView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; 如果有多個,就用“|”關聯。 還有一個屬性就是autoresizesSubviews,此屬性的意思就是,是否可以讓其subviews自動進行調整,默認狀態是YES,就是允許,如果設置成NO,那麼subView的autoresizingMask屬性失效。

AVCaptureDevice對flash light的操作

//改變FlashLightStatus -(void)changeFlashLightStatus{ // check if flashlight available AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; if ([device hasTorch] && [device hasFlash]){ [device lockForConfiguration:nil]; if (device.torchMode == AVCaptureTorchModeOff) { [device setTorchMode:AVCaptureTorchModeOn]; [device setFlashMode:AVCaptureFlashModeOn]; //torchIsOn = YES; //define as a variable/property if you need to know status } else { [device setTorchMode:AVCaptureTorchModeOff]; [device setFlashMode:AVCaptureFlashModeOff]; //torchIsOn = NO; } [device unlockForConfiguration]; } } 以下轉自http://www.helmsmansoft.com/index.php/archives/1525 在appstore的眾多的軟件之中,相信很多人都用過一款軟件 “手電筒” , 在iPhone中,利用設備上的閃光燈來開發手電筒功能,下面就說下具體的實現方法: 開啟閃光燈的API封裝在框架AVFoundation.framework中,所以要想實現此功能,首先就要在程序中加入此框架,然後引入頭 #import 先來看下這個框架內的一些內容: AVCaptureDevice.h主要用來獲取iphone一些關於相機設備的屬性。 前置和後置攝像頭 enum { AVCaptureDevicePositionBack = 1, AVCaptureDevicePositionFront = 2 }; typedef NSInteger AVCaptureDevicePosition; 閃光燈開關 enum { AVCaptureFlashModeOff = 0, AVCaptureFlashModeOn = 1, AVCaptureFlashModeAuto = 2 }; typedef NSInteger AVCaptureFlashMode; 手電筒開關 enum { AVCaptureTorchModeOff = 0, AVCaptureTorchModeOn = 1, AVCaptureTorchModeAuto = 2, }; typedef NSInteger AVCaptureTorchMode; 焦距調整 enum { AVCaptureFocusModeLocked = 0, AVCaptureFocusModeAutoFocus = 1, AVCaptureFocusModeContinuousAutoFocus = 2, }; typedef NSInteger AVCaptureFocusMode; 曝光量調節 enum { AVCaptureExposureModeLocked = 0, AVCaptureExposureModeAutoExpose = 1, AVCaptureExposureModeContinuousAutoExposure = 2, }; typedef NSInteger AVCaptureExposureMode; 白平衡 enum { AVCaptureWhiteBalanceModeLocked = 0, AVCaptureWhiteBalanceModeAutoWhiteBalance = 1, AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance = 2, }; typedef NSInteger AVCaptureWhiteBalanceMode; 下面實現一個最簡單的手電筒功能: - (void)viewDidLoad { [super viewDidLoad]; UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect]; btn.frame = CGRectMake(100, 100, 100, 40); btn.backgroundColor = [UIColor clearColor]; [btn setTitle:@"打開手電筒" forState:UIControlStateNormal]; [btn addTarget:self action:@selector(flashMode:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn]; // Do any additional setup after loading the view, typically from a nib. } - (void)flashMode:(id)sender { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; //This method returns the default device of the given media type currently available on the system. UIButton *btn = (UIButton *)sender; if([device hasTorch]){ //如果設備有手電筒功能 if (device.torchMode == AVCaptureTorchModeOff) //如果手電筒關閉 { [btn setTitle:@"關閉手電筒" forState:UIControlStateNormal]; [device lockForConfiguration:nil]; //鎖定配置 [device setTorchMode:AVCaptureTorchModeOn]; //開啟手電筒 [device unlockForConfiguration]; //解鎖 }else{ [btn setTitle:@"打開手電筒" forState:UIControlStateNormal]; [device lockForConfiguration:nil]; [device setTorchMode:AVCaptureTorchModeOff]; [device unlockForConfiguration]; } }else{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示信息" message:@"您的設備不支持此功能" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil]; [alert show]; [alert release]; } } 其他Media Type的聲明 NSString *const AVMediaTypeVideo; NSString *const AVMediaTypeAudio; NSString *const AVMediaTypeText; NSString *const AVMediaTypeClosedCaption; NSString *const AVMediaTypeSubtitle; NSString *const AVMediaTypeTimecode; NSString *const AVMediaTypeTimedMetadata; NSString *const AVMediaTypeMuxed; 若是要檢測裝置是否提供該功能,可以透過 - (BOOL)hasMediaType:(NSString *)mediaType 來取得 取得攝像頭後,我們可以透過 @property(nonatomic, readonly) BOOL hasTorch @property(nonatomic, readonly) BOOL hasFlash 來判斷該攝像頭是否有提供閃光燈 我是要持續開啟所以使用Torch Mode lockForConfiguration跟unlockForConfiguration是配對的API 呼叫lockForConfiguration就可以控制硬件了 控制完畢後要呼叫unlockForConfiguration [device setTorchMode: AVCaptureTorchModeOn]; [device setTorchMode: AVCaptureTorchModeOff]; 這兩行代碼,就是開關閃光燈的代碼 注意此代碼要在真機下作用

2013年4月12日 星期五

C C++ Objective-C的差異點

C++中.(點)     ->(箭號)     ::(雙冒號)的用法說明
點是用來取用物件中的結構變數structure
箭號是用來取用物件中的指標變數pointer
因為objective-C中所有都是指標變數,所以都直接用點的

雙冒號是取用物件內部的function
int MyClass::add(int a, int b)
{... }
或是用來取用本身的物件
::a
.h .m .mm檔的差別
.h為標頭檔
.m代表這個檔案裡面包含objective-C或是C的語法
.mm代表這個檔案裡面包含objective-C或是C++的語法

2013年4月8日 星期一

遠端推播

http://www.easyapns.com/
 //官方文件的中文翻譯版
http://blog.toright.com/archives/2806

//接收端


//在程式開始時註冊
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     //Registering for push notifications...
    [[UIApplication sharedApplication]
     registerForRemoteNotificationTypes:
     (UIRemoteNotificationTypeAlert |
      UIRemoteNotificationTypeBadge |
      UIRemoteNotificationTypeSound)];
    return YES;
}


//要在AppDelegate中實作下面三個delegate
#pragma mark ﹣ Push Notification Delegate

//完成註冊後,必須傳送device token等的相關資訊給你的provider(server)。token是必要的,因為APNs會利用token跟device做溝通,當然,你也可以傳送些額外的資訊。
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    NSString *str = [NSString stringWithFormat:@"Device Token=%@",deviceToken];      
    NSLog(@"%@",str);
}

//傳送過程中若有發生錯誤,會反應在底下這個method。
 - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
     NSString *str = [NSString stringWithFormat: @"Error: %@", err]; NSLog(@"%@",str);
}

//APP正常運行或背景執行時:
//主要都是利用這個method做處理,只是需要經過ㄧ些判斷。
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    for (id key in userInfo) {
       NSLog(@"key: %@, value: %@", key, [userInfo objectForKey:key]);
    }
}

加入UIDatePicker以及選取時的觸發事件

- (void)viewDidLoad { [super viewDidLoad]; //加入UIDatePicker datePicker = [[UIDatePicker alloc] init]; [datePicker addObserver:self forKeyPath:@"date" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; [self.eventDateTextField setInputView:datePicker]; //NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; //[dateFormatter setDateFormat:@"MM/dd"]; //NSString *str = [dateFormatter stringFromDate:[datePicker date]]; //self.eventDateTextField.text = str; [datePicker addTarget:self action:@selector(dateIsChanged:) forControlEvents:UIControlEventValueChanged]; NSLog(@"%@",datePicker.date); } - (void)dateIsChanged:(id)sender{ NSLog(@"Date changed"); }

2013年4月2日 星期二

通知

//點擊後取消所有的編輯狀態
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//可以直接寫在UIViewController當中
{
 [self.view endEditing:YES];
}

 //利用textField的delegate Implement UITextFieldDelegate: @interface yourClass : UIViewController Use the delegate method: 
- (BOOL)textFieldShouldReturn:(UITextField *)textField{ 

 //do whatever you need here 
 //maybe you have several textFields, so first check which one was hit the return key: 
 if(textField == outlet_to_your_textField_1){ //do this }
 else if(textField == outlet_to_your_textField_2){ //do that }
 else if .... blablabla and so on return YES; 
}

連線到含有中文的網址

//要把整個網址轉碼成UTF8的格式,降子在eventLocationTextField輸入中文到網址中才不會錯 - (IBAction)callWebMap:(id)sender {

 NSString* txt = [[NSString alloc] initWithString:self.eventLocationTextField.text];
 NSString* query = [NSString stringWithFormat:@"http://maps.apple.com/maps?q=%@",txt]; 
NSString* strUrl = [query stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 
NSURL* url = [NSURL URLWithString:strUrl]; [[UIApplication sharedApplication] openURL:url]; 
[txt release];
 [query release];
 [strUrl release];
 [url release];

}

在tableCell中加入button的 方式

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //在cell.accessoryView加入button UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [button addTarget:self action:@selector(tipAction:) forControlEvents:UIControlEventTouchDown]; button.tag=indexPath.row+1; [button setTitle:@"Show View" forState:UIControlStateNormal]; button.frame = CGRectMake(20.0, 20.0, 60.0, 40.0); cell.accessoryView = button; //在cell.contentView加入button UIButton *infoButton = [UIButton buttonWithType:102]; [infoButton setFrame:CGRectMake(10,10,infoButton.frame.size.width, infoButton.frame.size.height)]; [infoButton addTarget:self action:@selector(infoButtonTapped) forControlEvents:UIControlEventTouchUpInside]; [cell.contentView addSubview:infoButton]; //在cell.imageView加入button [cell.imageView setImage:[UIImage imageNamed:@"info-button.png"]]; [cell.imageView setUserInteractionEnabled:YES]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(infoImageTapped:)]; [tap setNumberOfTapsRequired:1]; [cell.imageView setGestureRecognizers:[NSArray arrayWithObject:tap]]; [tap release]; }

2013年4月1日 星期一

循環uiscroll﹠page view

1,在viewcontroller先宣告 
2,在viewdidload //新增自己的View 
 MyView=self.advertisementView; //MyView = [[[UIView alloc] initWithFrame:CGRectMake(0.f, 0.f, self.view.frame.size.width, 180.f)]autorelease]; self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(10.f, 10.f, self.view.frame.size.width-20.f, 150.f) ]; self.pageControl = [[UIPageControl alloc]initWithFrame:CGRectMake(0.f, 160.f, self.view.frame.size.width, 15.f)]; // _colors = [[NSArray alloc]initWithObjects: [UIColor redColor], [UIColor greenColor], [UIColor blueColor], [UIColor yellowColor], [UIColor orangeColor], nil]; self.pagesArray = [[NSMutableArray alloc] initWithArray:_colors]; //ScrollView 的內容總尺吋 : 原寬 x 有幾個分頁, 高不變 self.scrollView.backgroundColor = [UIColor clearColor]; self.scrollView.delegate = self; self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width * 3, self.scrollView.frame.size.height); //設定是否啟動分頁機制 : 如不啟動,則會一直滑動不停 ; 如啟動,會一格一格的分頁顯示 self.scrollView.pagingEnabled = YES; self.scrollView.showsHorizontalScrollIndicator = NO; self.scrollView.showsVerticalScrollIndicator = NO; //設定 PageControl 的樣式 self.pageControl.backgroundColor = [UIColor clearColor]; self.pageControl.numberOfPages = self.pagesArray.count; self.pageControl.currentPage = 0; //設定 PageControl 的分頁數 self.pageControl.numberOfPages = self.pagesArray.count; self.pageControl.currentPage = 0; MyView.backgroundColor = [UIColor clearColor]; CGRect frame1; //定位每一個分頁 View 在 ScrollView 裡的位置( X 軸 ) frame1.origin.x = self.scrollView.frame.size.width * 0; // Y 軸不變 frame1.origin.y = 0; frame1.size = self.scrollView.frame.size; subview1 = [[UIView alloc] initWithFrame:frame1]; subview1.backgroundColor = [self.colors objectAtIndex:4]; CGRect frame2; //定位每一個分頁 View 在 ScrollView 裡的位置( X 軸 ) frame2.origin.x = self.scrollView.frame.size.width * 1; // Y 軸不變 frame2.origin.y = 0; frame2.size = self.scrollView.frame.size; subview2 = [[UIView alloc] initWithFrame:frame2]; subview2.backgroundColor = [self.colors objectAtIndex:0]; CGRect frame3; //定位每一個分頁 View 在 ScrollView 裡的位置( X 軸 ) frame3.origin.x = self.scrollView.frame.size.width * 2; // Y 軸不變 frame3.origin.y = 0; frame3.size = self.scrollView.frame.size; subview3 = [[UIView alloc] initWithFrame:frame3]; subview3.backgroundColor = [self.colors objectAtIndex:1]; //使用QuartzCore.framework替UIView加上圓角 //[subview1.layer setCornerRadius:15.0]; [_scrollView addSubview:subview1 ]; [_scrollView addSubview:subview2 ]; [_scrollView addSubview:subview3 ]; [self.scrollView scrollRectToVisible:CGRectMake(300.f, 0.f, self.scrollView.frame.size.width, self.scrollView.frame.size.height) animated:NO]; [MyView addSubview:self.scrollView]; [MyView addSubview:self.pageControl]; 

 3,實作uiscrollviewdelegate
 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ NSLog(@"%d",self.pageControl.numberOfPages); 
 if (self.scrollView.contentOffset.x == 0.f) { 
 if (self.pageControl.currentPage==0) { self.pageControl.currentPage = self.pageControl.numberOfPages-1; }else{ self.pageControl.currentPage --; } }else if(self.scrollView.contentOffset.x == 600.f){ if (self.pageControl.currentPage==self.pageControl.numberOfPages-1) { self.pageControl.currentPage = 0; }else{ self.pageControl.currentPage++; } } int lastPage,currentPage,nextPage; currentPage = self.pageControl.currentPage; if (self.pageControl.currentPage==self.pageControl.numberOfPages-1) { lastPage = currentPage -1; nextPage = 0; }else if(self.pageControl.currentPage==0){ lastPage = self.pageControl.numberOfPages-1; nextPage = currentPage +1; }else{ lastPage = currentPage -1; nextPage = currentPage +1; } NSLog(@"next %d",nextPage); NSLog(@"current %d",currentPage); NSLog(@"last %d",lastPage); subview1.backgroundColor = [_colors objectAtIndex:lastPage]; subview2.backgroundColor = [_colors objectAtIndex:currentPage]; subview3.backgroundColor = [_colors objectAtIndex:nextPage]; NSLog(@"%d",self.pageControl.currentPage); [self.scrollView scrollRectToVisible:CGRectMake(300.f, 0.f, self.scrollView.frame.size.width, self.scrollView.frame.size.height) animated:NO]; }

.h宣告的變數,在.m要alloc跟init不然會出錯

.h
@property(retain,nonatomic)NSArray *colors;

.m
_colors = [[NSArray alloc]initWithObjects: [UIColor redColor], [UIColor greenColor], [UIColor blueColor], [UIColor yellowColor], [UIColor orangeColor], nil];
 可以使用不會出錯 但若沒有使用alloc init

_colors = [NSArray ArrayWithObjects: [UIColor redColor], [UIColor greenColor], [UIColor blueColor], [UIColor yellowColor], [UIColor orangeColor], nil];
那只有在單一function可以用, 在其他的function呼叫_colors就會SIGABRT

實作APP開場影片

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. NSLog(@"didFinishLaunchingWithOptions"); NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"newhouse" ofType:@"MOV"]]; self.moviePlayer = [[MPMoviePlayerController alloc] initWithContentURL:url]; //播放器參數設定 self.moviePlayer.controlStyle = MPMovieControlStyleNone; self.moviePlayer.scalingMode = MPMovieScalingModeAspectFill; self.moviePlayer.repeatMode = MPMovieRepeatModeNone; self.moviePlayer.view.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin|UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleTopMargin; //設定播放器大小 [self.moviePlayer.view setFrame:self.window.rootViewController.view.bounds]; [self.window.rootViewController.view addSubview:self.moviePlayer.view]; //播放 [self.moviePlayer play]; //註冊通知中心,當影片播放完畢,進行回呼方法(playbackStateDidFinish) [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackStateDidFinish) name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer ]; //Hide video controls self.moviePlayer.controlStyle = MPMovieControlStyleNone; //Play as soon as loaded self.moviePlayer.shouldAutoplay = YES; return YES; } - (void)playbackStateDidFinish{ //註銷通知中心 [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:self.moviePlayer]; //停止播放 [self.moviePlayer stop]; //移除播放器 [self.moviePlayer.view removeFromSuperview]; }

Could not find a storyboard named 'MainStoryboard' in bundle NSBundle

去Info.plist把Main storyboard file base name改為現在的storyboard名稱即可