手指在屏幕上能達到的精度和鼠標指針有很大的不同。當用戶觸擊屏幕時,接觸
區域實際上是橢圓形的,而且比用戶想像的位置更靠下一點。根據觸摸屏幕的手指、手指的尺寸、手指接觸屏幕的力量、手指的方向、以及其它因素的不同,其“接觸部位”的尺寸和形狀也有所不同。底層的多點觸摸系統會分析所有的這些信息,爲您計算出單一的觸點。
UIResponder 是所有響應者對象的基類,
它不僅爲事件處理,而且也爲常見的響應者行爲定義編程接口。UIApplication、UIView、和所有從UIView 派生出來的UIKit 類(包括UIWindow)都直接或間接地繼承自UIResponder類。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
NSUInteger numTaps = [touch tapCount];
if (numTaps < 2) {
[self.nextResponder touchesBegan:touches withEvent:event];
} else {
[self handleDoubleTap:touch];
}
}
缺省情況下,視圖會接收觸摸事件。但是,您可以將其userInteractionEnabled
屬性聲明設置爲NO,關閉事件傳遞的功能。
在一定的時間內關閉事件的傳遞。應用程序可以調用UIApplication 的
beginIgnoringInteractionEvents 方法,並在隨後調用endIgnoringInteractionEvents 方法來實現這個目的。
缺省情況下,視圖只接收多點觸摸序列的第一個觸摸事件,而忽略
所有其它事件。如果您希望視圖處理多點觸摸,就必須使它啓用這個功能。在代碼或Interface Builder 的查看器窗口中將視圖的multipleTouchEnabled 屬性設置爲YES,就可以實現這個目標。
將事件傳遞限制在某個單獨的視圖上。缺省情況下,視圖的exclusiveTouch 屬性被設置爲NO。將這個屬性設置爲YES 會使相應的視圖具有這樣的特性:即當該視圖正在跟蹤觸摸動作時,窗口中的其它視圖無法同時進行跟蹤,它們不能接收到那些觸摸事件。
多點觸摸:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
當一個或多個手指觸碰屏幕時,發送touchesBegan:withEvent:消息。
當一個或多個手指在屏幕上移動時,發送touchesMoved:withEvent:消息。
當一個或多個手指離開屏幕時,發送touchesEnded:withEvent:消息。
當觸摸序列被諸如電話呼入這樣的系統事件所取消時,發送touchesCancelled:withEvent:消息。
上面這些方法都和特定的觸摸階段(比如UITouchPhaseBegan)相關聯,該信息存在於UITouch 對象的phase 屬性聲明中。
爲了處理給定階段的事件,響應者對象常常從傳入的集合參數中取得一或多個UITouch 對象,然後考察這些對象的屬性或取得它們的位置(如果需要處理所有觸摸對象,可以向該NSSet 對象發送anyObject 消息)。UITouch 類中有一個名爲locationInView:的重要方法,如果傳入self 參數值,它會給出觸摸動作在響應者座標系統中的位置(假定該響應者是一個UIView 對象,且傳入的視圖參數不爲nil)。另外,還有一個與之平行的方法,可以給出觸摸動作之前位置(previousLocationInView:)。UITouch 實例的屬性還可以給出發生多少次觸
碰(tapCount)、觸摸對象的創建或最後一次變化發生在什麼時間(timestamp)、以及觸摸處於什麼階段(phase)。
- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 2) {
CGPoint tapPoint = [theTouch locationInView:self];
// Process a double-tap gesture
}
}
在touchesEnded:withEvent:方法中,當觸擊次數爲一時,響應者對象就向自身發送一個performSelector:withObject:afterDelay:消息,其中的選擇器標識由響應者對象實現的、用於處理單擊手勢的方法;第二個參數是一個NSValue 或NSDictionary 對象,用於保存相關的UITouch 對象;時延參數則表示單擊和雙擊手勢之間的合理時間間隔。
在touchesBegan:withEvent:方法中,如果觸擊次數爲二,響應者對象會向自身發送一個cancelPreviousPerformRequestsWithTarget:消息,取消當前被掛起和延期執行的調用。如果觸碰次數不爲二,則在指定的延時之後,先前步驟中由選擇器標識的方法就會被調用,以處理單擊手勢。
在視圖中跟蹤碰擦手勢
#define HORIZ_SWIPE_DRAG_MIN 12
#define VERT_SWIPE_DRAG_MAX 4
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
startTouchPosition = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentTouchPosition = [touch locationInView:self];
// If the swipe tracks correctly.
if (fabsf(startTouchPosition.x - currentTouchPosition.x) >= HORIZ_SWIPE_DRAG_MIN
&&
fabsf(startTouchPosition.y - currentTouchPosition.y) <=
VERT_SWIPE_DRAG_MAX)
{
// It appears to be a swipe.
if (startTouchPosition.x < currentTouchPosition.x)
[self myProcessRightSwipe:touches withEvent:event];
else
[self myProcessLeftSwipe:touches withEvent:event];
}
else
{
// Process a non-swipe event.
}
}
處理複雜的多點觸摸序列
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
// Only move the placard view if the touch was in the placard view
if ([touch view] != placardView) {
// On double tap outside placard view, update placard's display string
if ([touch tapCount] == 2) {
[placardView setupNextDisplayString];
}
return;
}
// "Pulse" the placard view by scaling up then down
// Use UIView's built-in animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2);
placardView.transform = transform;
[UIView commitAnimations];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.5];
transform = CGAffineTransformMakeScale(1.1, 1.1);
placardView.transform = transform;
[UIView commitAnimations];
// Move the placardView to under the touch
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.25];
placardView.center = [self convertPoint:[touch locationInView:self]
fromView:placardView];
[UIView commitAnimations];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
// If the touch was in the placardView, move the placardView to its location
if ([touch view] == placardView) {
CGPoint location = [touch locationInView:self];
location = [self convertPoint:location fromView:placardView];
placardView.center = location;
return;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
// If the touch was in the placardView, bounce it back to the center
if ([touch view] == placardView) {
// Disable user interaction so subsequent touches don't interfere with animation
self.userInteractionEnabled = NO;
[self animatePlacardViewToCenter];
return;
}
}
在事件處理代碼中,您可以將觸摸狀態的相關位置保存下來,以便在必要時和變化之後的UITouch 實例進行比較。定製視圖可以用UIView 的hitTest:withEvent:方法或CALayer 的hitTest:方法來尋找接收觸摸事件的子視圖或層,進而正確地處理事件。下面的例子用於檢測定製視圖的層中的“Info” 圖像是否被觸碰。
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
CGPoint location = [[touches anyObject] locationInView:self];
CALayer *hitLayer = [[self layer] hitTest:[self convertPoint:location
fromView:nil]];
if (hitLayer == infoImage) {
[self displayInfo];
}
}
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
if ([touches count] == [[event touchesForView:self] count]) {
// last finger has lifted....
}
}
觸摸事件中包含一個觸摸對象的集合及其相關的狀態,而運動事件中除了事件類型、子類型、和時間戳之外,沒有其它狀態。系統以這種方式來解析運動手勢,避免和方向變化事件造成衝突。
爲了處理運動事件,UIResponder 的子類必須實現motionBegan:withEvent: 或
motionEnded:withEvent:方法之一,或者同時實現這兩個方法
UIKit 框架在UITextView、UITextField、和UIWebView 類中實現了拷貝-剪切-粘貼支持。如果您希望在自己的應用程序中得到這個行爲,可以使用這些類的對象,或者自行實現。
UIPasteboard 類提供了粘貼板的接口。粘貼板是用於在一個應用程序內或不同應用程序間進行數據共享的受保護區域。該類提供了讀寫剪貼板上數據項目的方法。
UIMenuController 類可以在選定的拷貝、剪切、和粘貼對象的上下方顯示一個編輯菜單。編輯菜單上的命令可以有拷貝、剪切、粘貼、選定、和全部選定。
UIResponder 類聲明瞭canPerformAction:withSender:方法。響應者類可以實現這個方法,以根據當前的上下文顯示或移除編輯菜單上的命令。
UIResponderStandardEditActions 非正式協議聲明瞭處理拷貝、剪切、粘貼、選定、和全部選定命令的接口。當用戶觸碰編輯菜單上的某個命令時, 相應的
UIResponderStandardEditActions 方法就會被調用。
UIPasteboardNameGeneral 用於剪切、拷貝、和粘貼操作,涉及到廣泛的數據類型。您可以通過該類的generalPasteboard 類方法來取得代表通用(General)粘貼板的單件對象。
UIPasteboardNameFind 用於檢索操作。當前用戶在檢索條(UISearchBar)鍵入的字符串會被寫入到這個粘貼板中, 因此可以在不同的應用程序中共享。您可以通過調用pasteboardWithName:create:類方法,並在名字參數中傳入UIPasteboardNameFind 值來取得代表檢索粘貼板的對象。典型情況下, 您只需使用系統定義的粘貼板就夠了。但在必要時, 您也可以通過pasteboardWithName:create: 方法來創建自己的應用程序粘貼板。如果您調用pasteboardWithUniqueName 方法,UIPasteboard 會爲您提供一個具有唯一名稱的應用程序粘貼板。您可以通過其name 屬性聲明來取得這個名稱。
調用UIMenuController 的sharedMenuController 類方法來取得全局的,即菜單控制器實例。
計算選定內容的邊界,並用得到的邊界矩形調用setTargetRect:inView:方法。系統會根據選定內容與屏幕頂部和底部的距離,將編輯菜單顯示在該矩形的上方或下方。
調用setMenuVisible:animated:方法(兩個參數都傳入YES),在選定內容的上方或下方動畫顯示編輯菜單。
程序清單3-4演示瞭如何在touchesEnded:withEvent:方法的實現中顯示編輯菜單(注意,例子中省略了處理選擇的代碼)。在這個代碼片段中, 定製視圖還向自己發送一個becomeFirstResponder 消息,確保自己在隨後的拷貝、剪切、和粘貼操作中是第一響應者。
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *theTouch = [touches anyObject];
if ([theTouch tapCount] == 2 && [self becomeFirstResponder]) {
// selection management code goes here...
// bring up editing menu.
UIMenuController *theMenu = [UIMenuController sharedMenuController];
CGRect selectionRect = CGRectMake(currentSelection.x, currentSelection.y, SIDE,SIDE);
[theMenu setTargetRect:selectionRect inView:self];
[theMenu setMenuVisible:YES animated:YES];
}
}
有條件地激活菜單命令
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
BOOL retValue = NO;
ColorTile *theTile = [self colorTileForOrigin:currentSelection];
if (action == @selector(paste:) )
retValue = (theTile == nil) &&
[[UIPasteboard generalPasteboard]
containsPasteboardTypes:
[NSArray arrayWithObject:ColorTileUTI]];
else if ( action == @selector(cut:) || action == @selector(copy:) )
retValue = (theTile != nil);
else
retValue = [super canPerformAction:action
withSender:sender];
return retValue;
}
請注意,這個方法的最後一個else 子句調用了超類的實現,使超類有機會處理子類忽略的命令。
拷貝和剪切操作
- (void)copy:(id)sender {
UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
ColorTile *theTile = [self colorTileForOrigin:currentSelection];
if (theTile) {
NSData *tileData = [NSKeyedArchiver
archivedDataWithRootObject:theTile];
if (tileData)
[gpBoard setData:tileData forPasteboardType:ColorTileUTI];
}
}
- (void)cut:(id)sender {
[self copy:sender];
ColorTile *theTile = [self colorTileForOrigin:currentSelection];
if (theTile) {
CGPoint tilePoint = theTile.tileOrigin;
[tiles removeObject:theTile];
CGRect tileRect = [self rectFromOrigin:tilePoint inset:TILE_INSET];
[self setNeedsDisplayInRect:tileRect];
}
}
將粘貼板的數據粘貼到選定位置上
- (void)paste:(id)sender {
UIPasteboard *gpBoard = [UIPasteboard generalPasteboard];
NSArray *pbType = [NSArray arrayWithObject:ColorTileUTI];
ColorTile *theTile = [self colorTileForOrigin:currentSelection];
if (theTile == nil && [gpBoard containsPasteboardTypes:pbType]) {
NSData *tileData = [gpBoard dataForPasteboardType:ColorTileUTI];
ColorTile *theTile = (ColorTile *)[NSKeyedUnarchiver
unarchiveObjectWithData:tileData];
if (theTile) {
theTile.tileOrigin = self.currentSelection;
[tiles addObject:theTile];
CGRect tileRect = [self rectFromOrigin:currentSelection inset:TILE_INSET];
[self setNeedsDisplayInRect:tileRect];
}
}
}
Quartz 是主要的描畫接口,支持基於路徑的描畫、
抗鋸齒渲染、漸變填充模式、圖像、顏色、座標空間變換、以及PDF 文檔的創建、顯示、和分析。UIKit 爲Quartz 的圖像和顏色操作提供了Objective-C 的封裝。Core Animation 爲很多UIKit 的視圖屬性聲明的動畫效果提供底層支持,也可以用於實現定製的動畫。
在調用您提供的drawRect:方法之前,視圖對象會自動配置其描畫環境,使您的代碼可以立即進行描畫。作爲這些配置的一部分,UIView 對象會爲當前描畫環境創建一個圖形上下文(對應於CGContextRef 封裝類型)
用戶座標空間是您發出的所有描畫命令的工作環境。該空間的單位由點來表示。設備座標空間指的是設備內在的座標空間,由像素來表示。缺省情況下,用戶座標空間上的一個點等於設備座標空間的一個像素,這意味着一個點等於1/160英寸。然而,您不應該假定這個比例總是1:1。
UIColor 對象提供了一些便利方法,用於通過RGB、HSB、和灰度值指定顏色值。
您也可以使用Core Graphics 框架中的CGContextSetRGBStrokeColor 和
CGContextSetRGBFillColor 函數來創建和設置顏色。
可移植網絡圖像格式(PNG) .png
標記圖像文件格式(TIFF) .tiff, .tif
聯合影像專家組格式(JPEG) .jpeg, .jpg
圖形交換格式(GIF) .gif
視窗位圖格式(DIB) .bmp, .BMPf
視窗圖標格式.ico
視窗光標.cur
XWindow 位圖.xbm
UIImage, 一個不可變類,用於圖像顯示。
UIColor, 爲設備顏色提供基本的支持。
UIFont, 爲需要字體的類提供字體信息。
UIScreen, 提供屏幕的基本信息。
NSString* imagePath = [[NSBundle mainBundle] pathForResource:@"myImage"
ofType:@"png"];
UIImage* myImageObj = [[UIImage alloc] initWithContentsOfFile:imagePath];
- (void)drawRect:(CGRect)rect
{
// Draw the image
[anImage drawAtPoint:CGPointMake(10,10)];
}
重要提示:如果您使用CGContextDrawImage 函數來直接描畫位圖,則在缺省情況下,圖像數據會上下倒置,因爲Quartz 圖像假定座標系統的原點在左下角,且座標軸的正向是向上和向右。雖然您可以在描畫之前對其進行轉換,但是將Quartz 圖像包裝爲一個UIImage 對象是更簡單的方法,這樣可以自動補償座標空間的差別。
UIKit 中的UIRectFrame 和UIRectFill 函數(以及其它函數)的功能是在視圖中描畫象矩形這樣的簡單路徑。
在創建路徑時,需要首先通過CGContextBeginPath 函數配置一個接收路徑命令的圖形上下文。調用該函數之後,就可以使用與路徑相關的函數來設置路徑的起始點,描畫直線和曲線,加入矩形和橢圓形等等。路徑的幾何形狀指定完成後,就可以直接進行描畫,或者將其引用存儲在CGPathRef 或CGMutablePathRef 數據類型中,以備後用。在視圖上描畫路徑時,可以描畫輪廓,也可以進行填充,或者同時進行這兩種操作。路徑輪廓可以用像CGContextStrokePath 這樣的函數來畫,即用當前的筆劃顏色畫出以路徑爲中心位置的線。路徑的填充則可以用CGContextFillPath 函數來實現,它的功能是用當前的填充顏色或樣式填充路徑線段包圍的區域。
Core Animation 的關鍵技術是層對象。層是一種輕量級的對象,在本質上類似於視圖,但實際上是模型對象,負責封裝顯示內容的幾何屬性、顯示時機、和視覺屬性變量。內容本身可以通過如下三種方式來提供:
您可以將一個CGImageRef 類型的數據賦值給層對象的contents 屬性變量。
您可以爲層分配一個委託,讓它負責描畫工作。
您可以從CALayer 派生出子類,並對其顯示方法進行重載。
UIKit 框架提供三個顯示文本內容的基本類:
UILabel 顯示靜態文本字符串
UITextField 顯示單行可編輯文本
UITextView 顯示多行可編輯文本
爲UITextField類提供支持的委託需要實現UITextFieldDelegate 協議定義的方法。類似地,爲UITextView類提供支持的委託需要實現UITextViewDelegate 協議定義的方法。
當鍵盤被顯示或隱藏的時候,iPhone OS 會向所有經過註冊的觀察者對象發出如下通告:
UIKeyboardWillShowNotification
UIKeyboardDidShowNotification
UIKeyboardWillHideNotification
UIKeyboardDidHideNotification
您可以通過調用視圖對象的becomeFirstResponder 方法來爲可編輯的文本視圖顯
示鍵盤。調用這個方法可以使目標視圖成爲第一響應者,並開始編輯過程,其效果和用戶觸擊該視圖是一樣的。
需要做的調整通常包括暫時調整一或多個視圖的尺寸和位置,從而使文本對象可見。管理帶
有鍵盤的文本對象的最簡單方法是將它們嵌入到一個UIScrollView ( 或其子類,如UITableView)對象。當鍵盤被顯示出來時,您需要做的只是調整滾動視圖的尺寸,並將目標文本對象滾動到合適的位置。爲此,在UIKeyboardDidShowNotification 通告的處理代碼中需要進行如下操作:
取得鍵盤的尺寸。
將滾動視圖的高度減去鍵盤的高度。
將目標文本框滾動到視圖中。
在配置滾動視圖時,請務必爲所有的內容視圖配置恰當的自動尺寸調整規則。在之
前的圖中,文本框實際上是一個UIView 對象的子視圖,該UIView 對象又是UIScrollView對象的子視圖。如果該UIView 對象的UIViewAutoresizingFlexibleWidth 和UIViewAutoresizingFlexibleHeight 選項被設置了,則改變滾動視圖的邊框尺寸會同時改變它的邊框,因而可能導致不可預料的結果。禁用這些選項可以確保該視圖保持尺寸不變,並正確滾動。
處理鍵盤通告
// Call this method somewhere in your view controller setup code.
- (void)registerForKeyboardNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShown:)
name:UIKeyboardDidShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasHidden:)
name:UIKeyboardDidHideNotification object:nil];
}
// Called when the UIKeyboardDidShowNotification is sent.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
if (keyboardShown)
return;
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info
objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Resize the scroll view (which is the root view of the window)
CGRect viewFrame = [scrollView frame];
viewFrame.size.height -= keyboardSize.height;
scrollView.frame = viewFrame;
// Scroll the active text field into view.
CGRect textFieldRect = [activeField frame];
[scrollView scrollRectToVisible:textFieldRect animated:YES];
keyboardShown = YES;
}
// Called when the UIKeyboardDidHideNotification is sent
- (void)keyboardWasHidden:(NSNotification*)aNotification
{
NSDictionary* info = [aNotification userInfo];
// Get the size of the keyboard.
NSValue* aValue = [info
objectForKey:UIKeyboardBoundsUserInfoKey];
CGSize keyboardSize = [aValue CGRectValue].size;
// Reset the height of the scroll view to its original value
CGRect viewFrame = [scrollView frame];
viewFrame.size.height += keyboardSize.height;
scrollView.frame = viewFrame;
keyboardShown = NO;
}
跟蹤活動文本框的方法
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
activeField = textField;
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
activeField = nil;
}
原文地址:http://www.2cto.com/kf/201108/99935.html