I os 152. 課程⼤大綱
• 觸控 Touch
• QV015:Touch
• QV071:BMI 的計算
• ⼿手勢 Gesture
• QV016:Gesture
• Samples:Touches
• 三軸加速器 Accelerometer
• QV023:Accelerometer
• QV024:延伸上述應⽤用 (球的平衡感)
• QV025:偵測⼿手機的搖動
• 陀螺儀 Gyroscope
• Samples:Erica_Gyro
• 相機
• QV173:相機擷取影像及存檔
7. 觸控事件的狀態 • UITouchPhaseBegan
• UITouchPhaseMoved
• UITouchPhaseStationary
• UITouchPhaseEnded
• UITouchPhaseCancelled
9. • 取得觸控資訊
• locationInView (傳回 CGPoint)
• topCount
• ......
• 多點觸控時
• setMultipleTouchEnabled (YES)
11. #import <UIKit/UIKit.h>
ViewController.h
#define X_SWIPE_DRAG_MIN 15
#define X_SWIPE_DRAG_MAX 4
#define Y_SWIPE_DRAG_MIN 15
#define Y_SWIPE_DRAG_MAX 4
@interface ViewController : UIViewController
{
IBOutlet UILabel *touchLabel;
IBOutlet UILabel *countLabel;
IBOutlet UILabel *slideLabel;
CGPoint pointStart, pointCurrent;
}
@property(retain, nonatomic) IBOutlet UILabel *touchLabel;
@property(retain, nonatomic) IBOutlet UILabel *countLabel;
-(void)handleTouch:(NSSet *) touches : (int) type;
-(void)slideUp:(NSSet *)touches;
-(void)slideDown:(NSSet *)touches;
-(void)slideLeft:(NSSet *)touches;
-(void)slideRight:(NSSet *)touches;
@end
13. ViewController.m (1/4)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLabel.text = @"觸控開始 (touchBegin)";
[self handleTouch:touches :1];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLabel.text = @"觸控移動 (touchMoved)";
[self handleTouch:touches :2];
// 省略部分程式
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLabel.text = @"觸控結束 (touchEnd)";
[self handleTouch:touches :3];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLabel.text = @"觸控取消 (touchCancelled)";
[self handleTouch:touches :4];
}
14. ViewController.m (2/4)
-(void)handleTouch:(NSSet *) touches : (int) type
{
UITouch *myTouch = [touches anyObject];
NSUInteger numTaps = [myTouch tapCount];
switch (type)
{
case 1:
countLabel.text = [[NSString alloc]
initWithFormat:@"觸控 %d 次", numTaps];
pointStart = [myTouch locationInView:self.view];
break;
case 2:
pointCurrent = [myTouch locationInView:self.view];
countLabel.text = [[NSString alloc]
initWithFormat:@"(%3.0f, %3.0f)", pointCurrent.x, pointCurrent.y];
break;
case 3:
// 省略
break;
case 4:
// 省略
break;
default:
break;
}
}
15. ViewController.m (3/4)
// 增加⽅方向的判斷
if(fabsf(pointCurrent.x-pointStart.x)>=X_SWIPE_DRAG_MIN &&
fabsf(pointCurrent.y-pointStart.y)<=X_SWIPE_DRAG_MAX)
{
if(pointCurrent.x > pointStart.x)
{
[self slideRight:touches];
}
else
{
[self slideLeft:touches];
}
}
if(fabsf(pointCurrent.y-pointStart.y)>=Y_SWIPE_DRAG_MIN &&
fabsf(pointCurrent.x-pointStart.x)<=X_SWIPE_DRAG_MAX)
{
if(pointCurrent.y > pointStart.y)
{
[self slideDown:touches];
}
else
{
[self slideUp:touches];
}
}
16. ViewController.m (4/4)
-(void)slideUp:(NSSet *)touches
{
slideLabel.text = @"UP";
}
-(void)slideDown:(NSSet *)touches
{
slideLabel.text = @"DOWN";
}
-(void)slideLeft:(NSSet *)touches
{
slideLabel.text = @"LEFT";
}
-(void)slideRight:(NSSet *)touches
{
slideLabel.text = @"RIGHT";
}
17. 範例:
BMI 計算
(利⽤用觸控取得座標位置,同時輸⼊入 x,y 兩值)
18. Project QV071
BMI 計算
以觸控⽅方式同時輸⼊入⾝身⾼高
及體重,並⽴立即顯⽰示 BMI
除⽂文字外,圖⽚片寬度及⾼高
度也同時反映⾝身⾼高及體重
21. ViewController.m (1/2)
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint pointTouch;
UITouch *myTouch = [touches anyObject];
pointTouch = [myTouch locationInView:self.view];
float hMin = 100; // ⾝身⾼高最⼩小值
float hMax = 220; // ⾝身⾼高最⼤大值 重要的參數設定
float wMin = 20; // 體重最⼩小值
float wMax = 120; // 體重最⼤大值
float h = (pointTouch.y * (hMin-hMax) / 480) + hMax;
float w = (pointTouch.x * (wMax-wMin) / 320) + wMin;
float bmi = w / ((h/100)*(h/100));
// 處理顯⽰示的⽂文字
heightLabel.text = [NSString stringWithFormat:@"%3.0f", h];
weightLabel.text = [NSString stringWithFormat:@"%3.0f", w];
bmiLabel.text = [NSString stringWithFormat:@"%4.2f", bmi];
***** 此處省略部份程式碼 *****
}
22. ViewController.m (2/2)
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
***** 此處省略部份程式碼 *****
// 處理圖檔⼤大⼩小
float iwMin = 128; // 體重最⼩小時的圖檔寬度
float iwMax = 460; // 體重最⼤大時的圖檔寬度
float ihMin = 80; // ⾝身⾼高最⼩小時的圖檔⾼高度
float ihMax = 400; // ⾝身⾼高最⼤大時的圖檔⾼高度
float iw = iwMin + ((w-wMin) * (iwMax-iwMin) / (wMax-wMin));
float ih = ihMin + ((h-hMin) * (ihMax-ihMin) / (hMax-hMin));
personImageView.bounds = CGRectMake(0, 0, iw, ih);
personImageView.center = CGPointMake(160, 260);
}
調整圖⽚片的尺⼨寸
24. 六種⼿手勢
• tap 點擊 (多少根指頭及按幾下)
• pan 拖曳 (位置?)
• swipe 滑過 (向上、向下、向左、向右)
• pinch 兩指撥動 (往內捏縮、往外擴展)
• rotation 旋轉 (順時針、逆時針)
• long press ⻑⾧長按
28. ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
IBOutlet UILabel *myLabel;
}
@property (retain, nonatomic) IBOutlet UILabel *myLabel;
-(void)handleTap1;
-(void)handleTap2;
-(void)handleTap3;
-(void)handleTap4;
-(void)handleTap5;
-(void)handlePinch: (UIPinchGestureRecognizer *)recognizer;
-(void)handleSwipeRight: (UISwipeGestureRecognizer *)recognizer;
-(void)handleSwipeLeft: (UISwipeGestureRecognizer *)recognizer;
-(void)handleSwipeUp: (UISwipeGestureRecognizer *)recognizer;
-(void)handleSwipeDown: (UISwipeGestureRecognizer *)recognizer;
-(void)handleRotate: (UIRotationGestureRecognizer *)recognizer;
-(void)handleLongPress: (UIRotationGestureRecognizer *)recognizer;
@end
29. ViewController.m (1/6)
// 設定按⼀一下的⼿手勢
UITapGestureRecognizer *tapGesture1 = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleTap1)];
[self.view addGestureRecognizer:tapGesture1];
// 設定要按兩下的⼿手勢
UITapGestureRecognizer *tapGesture2 = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleTap2)];
tapGesture2.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:tapGesture2];
// 設定要兩根⼿手指的⼿手勢
UITapGestureRecognizer *tapGesture3 = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleTap3)];
tapGesture3.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:tapGesture3];
設定按下的次數及有幾根⼿手指
30. ViewController.m (2/6)
// 設定要兩根⼿手指、按兩下的⼿手勢
UITapGestureRecognizer *tapGesture4 = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleTap4)];
tapGesture4.numberOfTapsRequired = 2;
tapGesture4.numberOfTouchesRequired = 2;
[self.view addGestureRecognizer:tapGesture4];
// 設定要三根⼿手指、按兩下的⼿手勢
UITapGestureRecognizer *tapGesture5 = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleTap5)];
tapGesture5.numberOfTapsRequired = 3;
tapGesture5.numberOfTouchesRequired = 3;
[self.view addGestureRecognizer:tapGesture5];
-(void)handleTap1
{
NSString *myString = [[NSString alloc] initWithFormat:@"按⼀一下⼿手勢"];
myLabel.text = myString;
}
31. ViewController.m (3/6)
// 設定滑動⼿手勢 (Swipe Right)
UISwipeGestureRecognizer *swipeRightGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSwipeRight:)];
[self.view addGestureRecognizer:swipeRightGesture];
// 設定滑動⼿手勢 (Swipe Left)
UISwipeGestureRecognizer *swipeLeftGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSwipeLeft:)];
[swipeLeftGesture setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:swipeLeftGesture];
// 設定滑動⼿手勢 (Swipe Up)
UISwipeGestureRecognizer *swipeUpGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSwipeUp:)];
[swipeUpGesture setDirection:UISwipeGestureRecognizerDirectionUp];
[self.view addGestureRecognizer:swipeUpGesture];
// 設定滑動⼿手勢 (Swipe Down)
UISwipeGestureRecognizer *swipeDownGesture =
[[UISwipeGestureRecognizer alloc]
initWithTarget:self action:@selector(handleSwipeDown:)];
[swipeDownGesture setDirection: UISwipeGestureRecognizerDirectionDown];
[self.view addGestureRecognizer:swipeDownGesture];
32. ViewController.m (4/6)
// 設定捏縮⼿手勢 (Pinch)
UIPinchGestureRecognizer *pinchGesture =
[[UIPinchGestureRecognizer alloc]
initWithTarget:self
action:@selector(handlePinch:)];
[self.view addGestureRecognizer:pinchGesture];
-(void)handlePinch: (UIPinchGestureRecognizer *)recognizer
{
NSString *myString = [[NSString alloc] initWithFormat:
@"執⾏行捏縮⼿手勢 (Pinch scale: %f)", recognizer.scale];
myLabel.text = myString;
}
注意 scale 值...
⼤大於1表⽰示向外捏
⼩小於1表⽰示向內捏
33. ViewController.m (5/6)
// 設定旋轉⼿手勢
UIRotationGestureRecognizer *rotateGesture =
[[UIRotationGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleRotate:)];
[self.view addGestureRecognizer:rotateGesture];
-(void)handleRotate:(UIRotationGestureRecognizer *)recognizer
{
NSString *myString = [[NSString alloc] initWithFormat:
@"執⾏行旋轉⼿手勢。⾓角度 %f", [recognizer rotation]*180/M_PI];
myLabel.text = myString;
}
34. ViewController.m (6/6)
// 設定⻑⾧長按⼿手勢
UILongPressGestureRecognizer *longPressGesture =
[[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleLongPress:)];
[self.view addGestureRecognizer:longPressGesture];
-(void)handleLongPress:(UIRotationGestureRecognizer *)recognizer
{
NSString *myString = [[NSString alloc]
initWithFormat:@"你執⾏行了⻑⾧長按⼿手勢。"];
CGPoint location = [recognizer locationInView:[recognizer view]];
NSLog(@"%f , %f", location.x, location.y);
myLabel.text = myString;
}
36. 範例觀摩:Touches
The Touches sample application
demonstrates how to handle touches,
including multiple touches that move
multiple objects. After the application
launches, three colored pieces appear
onscreen that the user can move
independently.
"Touches_Classic" demonstrates how
to handle touches using
UIResponder's: touches began,
touches moved, and touches ended.
"Touches_GestureRecognizers"
demonstrates how to use
UIGestureRecognizers to handle
touch events.
41. ViewController.m (1/2)
- (void)loadView
{
// 設定主視圖
self.view = [[UIView alloc]
initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view.backgroundColor = [UIColor whiteColor];
// 產⽣生顯⽰示區
display = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 380)];
display.font = [UIFont fontWithName:@"Helvetica" size:36.0f];
display.textColor = [UIColor blackColor];
display.backgroundColor = [UIColor clearColor];
display.lineBreakMode = UILineBreakModeWordWrap;
display.numberOfLines = 0;
display.text = @"";
[self.view addSubview:display];
// 取得加速度感應器的實體
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 0.1;
accelerometer.delegate = self;
}
42. ViewController.m (2/2)
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:
(UIAcceleration *)acceleration
{
// 低階篩選程序 Low-pass Filter (重⼒力加速度)
gx = acceleration.x * 0.1 + gx * (1.0-0.1);
gy = acceleration.y * 0.1 + gy * (1.0-0.1);
gz = acceleration.z * 0.1 + gz * (1.0-0.1);
// ⾼高階篩選程序 High-pass Filter(瞬間加速度)
UIAccelerationValue ax, ay, az;
ax = acceleration.x - gx;
ay = acceleration.y - gy;
az = acceleration.z - gz;
// 顯⽰示
display.text = [NSString stringWithFormat:
@"X:%fnY:%fnZ:%fnnnX:%fnY:%fnZ:%fn",
gx, gy, gz, ax, ay, az];
}
45. ViewController.m (1/2)
- (void)loadView
{
[super loadView];
// 設定主視圖
self.view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view.backgroundColor = [UIColor whiteColor];
// 產⽣生顯⽰示區
display = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 380)];
display.font = [UIFont fontWithName:@"Helvetica" size:14.0f];
display.textColor = [UIColor blackColor];
display.backgroundColor = [UIColor clearColor];
display.lineBreakMode = UILineBreakModeWordWrap;
display.numberOfLines = 0;
display.text = @"";
[self.view addSubview:display];
// 產⽣生球的圖
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100f,200f,BALL_SIZE, BALL_SIZE)];
imageView.image = [UIImage imageNamed:@"ball.png"];
[self.view addSubview:imageView];
// 取得加速度感應器的實體
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 1.0 / 30;
accelerometer.delegate = self;
// 球的移動速度
ballDX = 0.0f;
ballDY = 0.0f;
}
46. - (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
// 低階篩選程序
gx = acceleration.x * RATIO + gx * (1.0-RATIO);
gy = acceleration.y * RATIO + gy * (1.0-RATIO);
gz = acceleration.z * RATIO + gz * (1.0-RATIO);
// ⾼高階篩選程序 ViewController.m (2/2)
UIAccelerationValue ax, ay, az;
ax = acceleration.x - gx;
ay = acceleration.y - gy;
az = acceleration.z - gz;
// 速度更新
ballDX += gx;
ballDY -= gy;
if(ballDX > MAX_SPEED) ballDX = MAX_SPEED;
if(ballDX < -MAX_SPEED) ballDX = -MAX_SPEED;
if(ballDY > MAX_SPEED) ballDY = MAX_SPEED;
if(ballDY < -MAX_SPEED) ballDY = -MAX_SPEED;
// 更新座標
float newX = imageView.center.x + ballDX;
float newY = imageView.center.y + ballDY;
imageView.center = CGPointMake(newX, newY);
// 若超出畫⾯面外,返回初始位置
if(newX<-BALL_SIZE/2 || newX>320+BALL_SIZE/2 || newY<-BALL_SIZE/2 || newY>480+BALL_SIZE/2)
{
imageView.center = CGPointMake(160, 240);
ballDX = 0.0f;
ballDY = 0.0f;
}
// 顯⽰示
display.text = [NSString stringWithFormat:@"X:%fnY:%fnZ:%fnnnX:%fnY:%fnZ:%fn",
gx, gy, gz, ax, ay, az];
}
49. ViewController.m (1/2)
#import "ViewController.h"
#define SHAKE_AX 1.5
@implementation ViewController
- (void)loadView
{
[super loadView];
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 0.1;
accelerometer.delegate = self;
}
// 省略部分程式
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
shaking = NO;
display.text = @"搖";
}
@end
50. ViewController.m (2/2)
- (void)accelerometer:(UIAccelerometer *)accelerometer
didAccelerate:(UIAcceleration *)acceleration
{
// 低階篩選程序 (重⼒力加速度)
gx = acceleration.x * 0.1 + gx * (1.0-0.1);
gy = acceleration.y * 0.1 + gy * (1.0-0.1);
gz = acceleration.z * 0.1 + gz * (1.0-0.1);
// ⾼高階篩選程序 (瞬間重⼒力加速度)
UIAccelerationValue ax, ay, az;
ax = acceleration.x - gx;
ay = acceleration.y - gy;
az = acceleration.z - gz;
// 只要瞬間加速度超過預設⾨門檻值就搖晃
if(!shaking && (ax>SHAKE_AX || ax<-SHAKE_AX))
{
shaking = YES;
display.text = @"!!!爆炸!!!";
}
}
56. ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
} 影像擷取代理協定
// 顯⽰示拍攝後影像 view
@property (nonatomic, retain) IBOutlet UIImageView *imageView;
// 儲存按鈕
@property (nonatomic, retain) IBOutlet UIBarButtonItem *saveImageButton;
// 在相機上顯⽰示拍攝畫⾯面
- (IBAction) showCameraAction:(id)sender;
// 把拍攝後影像儲存到 iPhone 相簿
- (IBAction) saveImageAction:(id)sender;
@end
58. ViewController.m (1/2)
- (IBAction) showCameraAction:(id)sender
{
UIImagePickerController *imagePickerController =
[[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePickerController.delegate = self;
imagePickerController.allowsEditing = YES;
[self presentModalViewController:imagePickerController animated:YES];
}
- (void) imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
imageView.image = image;
saveImageButton.enabled = YES;
[self dismissModalViewControllerAnimated:YES];
}
59. ViewController.m (2/2)
- (IBAction) saveImageAction:(id)sender
{
UIImage *image = imageView.image;
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
saveImageButton.enabled = NO;
}