iOS應用開發視頻教程筆記(十六)Action Sheets, Image Picker, Core Motion


這節課主要講NSTimer的內容以及它的替代方案“perform after delay”、更復雜的動畫、Alerts、Action Sheets、UIImagePickerController(用來從camera里取東西)和Core Motion。

NSTimer

先調用這個工廠方法scheduledTimerWithTimeInterval,如果它是一個重復timer,停止timer的方法是:

- (void)invalidate;

如果不是一個重復timer,不需要invalidate,它自己會結束。可以看到repeats是YES或NO,代表是否重復。如果有一個strong指針指向它,那么需要將其設置為nil;如果是一個weak指針,它自己就可能把自己nil掉。所以這是除了outlet之外,另一種可能會使用一個弱指針的情況。調用了scheduledTimerWithTimeInterval,在它運行的時候會有強指針指向它,一旦通過invalidate停止它,它就不會再有強指針。

Perform after Delay

這是個NSObject的實例方法,它讓一個對象執行這個參數為withObject的selector,也可以沒有參數,這種情況下withObject將是nil。在一定的afterDelay之后執行:

- (void)performSelector:(SEL)aSelector
             withObject:(id)argument 
             afterDelay:(NSTimeInterval)seconds;

它利用當前線程的run loop機制執行。不是每個線程都有一個run loop,主線程肯定有run loop,該run loop要處理很多東西。不是任意線程都可以執行Perform after Delay的,在本課程內只能在主線程執行。

這不是實時的,和NSTimer一樣,如果說延遲0.72秒后執行,run loop可能在忙,這種情況下它實際上直到0.74秒后才執行。它只是放到了run loop里,直到run loop空閑了並超過了該delay時間,它才會運行。有一點要注意,它永遠不會立即執行。離開屏幕的時候要確保它停止了。

[self.tableView performSelector:@selector(reloadData) withObject:nil afterDelay:0];

如何取消?在做了Perform after Delay之后,你在它完成前意識到“不想它發生”,需要調用這個類方法:

+ (void)cancelPreviousPerformRequestsWithTarget:(id)target
                                       selector:(SEL)aSelector 
                                         object:(id)object; 
+ (void)cancelPreviousPerformRequestsWithTarget:(id)target;

你指定消息、發起的對象、selector和參數,系統會去取消它。下面一個方法將取消該目標的所有執行要求。

沒有辦法找出它們來,沒有辦法查詢正在排隊的執行事件。

Alerts and Action Sheets

有兩種方式來提醒用戶:有一個叫action sheet,它從iphone的底部彈出或者在ipad的popover彈出,它提供了一個選項讓用戶決定下一步做什么;還有另一種提醒,叫做UIAlert,它更多用於異步發生的事情。

UIActionSheet

這是它的指定初始化方法:

    -(id)initWithTitle:(NSString *)title 
              delegate:(id <UIActionSheetDelegate>)delegate
     cancelButtonTitle:(NSString *)cancelButtonTitle 
destructiveButtonTitle:(NSString
*)destructiveButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...;

title出現在action sheet的頂部,用delegate來指導用戶的選擇,一個取消按鈕的標題,通常就是cancel,最后是其他按鈕的標題,想要幾個就幾個,以nil結尾。通常在初始化時就指定好所有的按鈕,但也可以用addButtonWithTitle動態添加:

- (void)addButtonWithTitle:(NSString *)buttonTitle;

創建之后,可以用以下顯示方法中的一個來顯示它:

UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:...];
[actionSheet showInView:(UIView*)]; //centerstheviewoniPad(don’tusethisoniPad) 
[actionSheet showFromRect:(CGRect) inView:(UIView *) animated:(BOOL)];    // good on iPad 
[actionSheet showFromBarButtonItem:(UIBarButtonItem *) animated:(BOOL)]; // good on iPad

showInView是顯示在iphone的中間位置,它會從下往上滑動;在ipad上它會放在中間,所以在ipad上從不用showInView。showFromRect,在ipad上指定個矩形區域,它會在其上顯示個popover。showFromBarButtonItem,就是popover會指向個BarButtonItem。在iphone上只能使用showInView,如果是universal app,要用些if來判斷。

用delegate來知道用戶選擇了什么:

- (void)actionSheet:(UIActionSheet *)sender clickedButtonAtIndex:(NSInteger)index;

這里的index用來和action sheet里的一些property對照,如cancel button或destructive button或title:

@property NSInteger cancelButtonIndex;      // don’t set this if you set it in initializer 
@property NSInteger destructiveButtonIndex; // don’t set this if you set it in initializer

還可以知道第一個按鈕的index是什么,然后可以做一些計算的東西,通常情況下,只需要比較字符串。

也可以用代碼來關閉action sheet,最常見和最重要的原因之一是,app被退到后台了:

- (void)dismissWithClickedButtonIndex:(NSInteger)index animated:(BOOL)animated;

如何知道即將進入后台?只要監聽UIApplicationDidEnterBackgroundNotification,popover要多考慮幾件事:

一,它沒有取消按鈕;

二,當popover action sheet來自UIBarButton,該toolbar會被添加到popover的passthroughViews,passthroughViews就是點擊之后該popover不會消失,這意味着如果有bar button,它們還會響應;

三,必須確保如果bar button被再次按下,不會出現第二個action sheet。

UIAlertView

它的初始化幾乎和action sheet一樣:

-(id)initWithTitle:(NSString *)title 
           message:(NSString *)message // different from UIActionSheet
          delegate:(id <UIActionSheetDelegate>)delegate 
 cancelButtonTitle:(NSString *)cancelButtonTitle 
 otherButtonTitles:(NSString *)otherButtonTitles, ...;

它有個message,比如網絡連接失敗之類的。此外,沒有destructive button。仍然可以添加更多button,用show來顯示:

- (void)addButtonWithTitle:(NSString *)buttonTitle;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:...]; 
[alertView show];    // different from UIActionSheet, always appears in center of screen

UIImagePickerController

這是你要如何從相機獲得圖像。這只是一個view controller,和其他的controller使用方法一樣。在iPad上,可以modally顯示它,會覆蓋整個屏幕或者可以放到popover上。

怎么實現呢?用alloc/init來創建它,然后配置它,配置主要是說你想要什么,比如視頻、圖片或查找photo library、還是要拍照等等,然后使用兩個方法來顯示出來,最后你響應這個會告訴你用戶選了什么的delegate方法。

用戶可以做什么很大程度上取決於他們的設備,通過它的類方法isSourceTypeAvailable來知道哪些是可用的:

+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType;

共有三種不同的source type:UIImagePickerControllerSourceTypePhotoLibrary/Camera/SavedPhotosAlbum。

即使設備有攝像頭,它也可能無法錄視頻,所以必須做第二次檢查:

+ (NSArray *)availableMediaTypesForSourceType:(UIImagePickerControllerSourceType)sourceType;

如果設備支持你選的source type,比如camera,然后你問它是否支持你要的media type,比如該camera是否能錄視頻。

只有兩種有用的source type:一個是image,一個是movie。必須import <MobileCoreServices/MobileCoreServices.h>,而且必須添加整個MobileCoreServices framework,否則無法使用kUTTypeImage和kUTTypeMovie這兩個常量。kUTTypeImage和kUTTypeMovie不是NSString,而是CFString,所以你要做強制轉換。

下面是個例子,如何設置source type和media type:

UIIPC *picker = [[UIIPC alloc] init]; 
picker.delegate=self; //self has to say it implements UINavigationControllerDelegate too:( 
if ([UIIPC isSourceTypeAvailable:UIIPCSourceTypeCamera]) {
       picker.sourceType = UIIPCSourceTypeCamera; 
} // else we’ll take what we can get (photo library by default)
NSString*desired=(NSString*)kUTTypeMovie; //e.g.,couldbekUTTypeImage
if ([[UIIPC availableMediaTypesForSourceType:picker.sourceType] containsObject:desired]) {
      picker.mediaTypes = [NSArray arrayWithObject:desired];
      // proceed to put the picker up
}else {
      // fail, we can’t get the type of media we want from the source we want
}

你在配置這個picker,來讓用戶選東西,如果用了camera或photo library,可以讓用戶有一個界面進行編輯,放大它、移動並截取部分等等。

@property BOOL allowsEditing;

可以限制視頻的數據量:

@property UIIPCQualityType videoQuality;

在用戶選好照片,編輯完之后,此delegate方法將被調用:

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
       // extract image/movie data/metadata here, more on the next slide 
       [self dismissModalViewControllerAnimated:YES]; // or popover dismissal
}

它有個很重要的參數info,我們要的所有信息都在info里。這里要注意的是,如果這是一個modalViewController,需要dismiss它。

它發了個cancel消息,必須關閉它:

- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
       [self dismissModalViewControllerAnimated:YES]; // or popover dismissal
}

info是個dictionary,左邊是key,右邊是value type。

UIImagePickerControllerMediaType          // kUTTypeImage or kUTTypeMovie
UIImagePickerControllerOriginalImage      // UIImage
UIImagePickerControllerEditedImage        // UIImage
UIImagePickerControllerCropRect           // CGRect (in an NSValue)
UIImagePickerControllerMediaMetadata      // NSDictionary info about the image to save later
UIImagePickerControllerMediaURL           // NSURL edited video
UIImagePickerControllerReferenceURL       // NSURL original (unedited) video

有一整套可以在camera上疊加東西的機制,可以實時地把自己的UI疊加到相機上,也可以有自己的照相按鈕。

Core Motion

Core Motion是一個用來訪問重力感應硬件的API集合,有加速度計、陀螺儀、磁力計。Accelerometer告訴你設備在3個空間維度里的加速度,Gyro告訴你設備的旋轉,Magnetometer告訴你很多關於設備在位置方面的信息,特別是設備幾秒前或幾毫秒前的位置。

和之前的相機一樣,要先了解設備能做什么,通過一個CMMotionManager類來訪問motion信息,它有個類方法shared motion manager,通過這個方法來獲得motion manager。

要怎么用motion manager?要先檢查看看有什么樣的硬件可用,然后簡單地輪詢它來獲取數據,或更好一些:把一個block放到一個queue上,以你需要的速率反饋回來最新的數據。有兩種方式,詢問或調用block。

怎么檢查傳感器的可用性?CMMotionManager有這些property:

@property (readonly) BOOL {accelerometer,gyro,magnetometer,deviceMotion}Available;

然后就可以用start update開始啟動硬件了:

- (void)start{Accelerometer,Gyro,Magnetometer,DeviceMotion}Updates;

還可以知道CMMotionManager當前是否在從特定的硬件收集數據:

@property (readonly) BOOL {accelerometer,gyro,magnetometer,deviceMotion}Active;

當你用完了,要停止它,事實上得盡可能少用這個東西,因為開銷很大,不用的時候別讓它在后台運行:

- (void)stop{Accelerometer,Gyro,Magnetometer,DeviceMotion}Updates;

CMDeviceMotion會分別告訴你加速度和重力加速度,它會幫你排除所有的設備錯誤。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM