ios app 開發 -- storybard 基礎練習、app 上架、iap
DESCRIPTION
iOS App 開發 -- Storybard 基礎練習 包含以下主題 1. Tab page 2. Page Control 3. Table 4. Gesture 5. Collection 6. Timer 跟 Thread 7. iAd 跟 IAPTRANSCRIPT
![Page 1: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/1.jpg)
iOS App Development
1
![Page 2: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/2.jpg)
2
資料下載
https://dl.dropboxusercontent.com/u/14692540/iphoneui.zip
![Page 3: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/3.jpg)
Who am I?林銘賢
白天網路公司工程師
晚上南台講師
其它時間導航 App Navier HUD iOS 版 作者
![Page 4: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/4.jpg)
Navier HUD iOS
![Page 5: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/5.jpg)
Navier HUD iOS
![Page 6: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/6.jpg)
Navier HUD iOS
![Page 7: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/7.jpg)
Navier HUD iOS
![Page 8: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/8.jpg)
8
課程大綱 iOS App 基礎
Tab Bar
Page Control
Table
Gesture
Collection
Timer and Thread
iAd and IAP
![Page 9: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/9.jpg)
9
iOS App Basics
![Page 10: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/10.jpg)
10
View 階層架構
![Page 11: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/11.jpg)
11
View 階層架構
![Page 12: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/12.jpg)
12
畫面坐標原點在左上角
![Page 13: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/13.jpg)
13
Frame 與 Bounds
![Page 14: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/14.jpg)
14
App 沙盒 (SandBox)
每個 App 都有自獨立的目錄
![Page 15: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/15.jpg)
15
常用目錄
目錄名稱 說明<Application_Home>/<AppName>.app App bundle 。不能在這個目錄
寫入任何資料<Application_Home>/Documents 存放一般資料,不會被系統清除<Application_Home>/Library 存放與 code 有關,但非使用
者資料的地方<Application_Home>/tmp 暫存的目錄,會被系統清除
![Page 16: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/16.jpg)
16
轉向直立
直立上下顛倒
橫向 - 左 橫向 - 右
![Page 17: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/17.jpg)
17
啟動 APP
![Page 18: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/18.jpg)
18
切換 App 執行進入背景模式
![Page 19: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/19.jpg)
19
從背景模式切換到前景模式
![Page 20: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/20.jpg)
20
![Page 21: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/21.jpg)
21
視窗架構
![Page 22: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/22.jpg)
22
視窗架構
實際上是由 View Controller 負責提供 UIView 給 Windows
![Page 23: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/23.jpg)
23
init
loadView
viewDidLoad
viewWillAppear
viewDidAppear
viewWillDisappear
viewDidDisappear
viewDidUnload
dealloc
View Controller 的生命週期
![Page 24: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/24.jpg)
24
View Controller 架構
![Page 25: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/25.jpg)
25
View Controller 架構
![Page 26: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/26.jpg)
26
![Page 27: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/27.jpg)
27
![Page 28: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/28.jpg)
28
![Page 29: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/29.jpg)
29
Navigation
Push
Pop
Push
Pop
![Page 30: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/30.jpg)
30
![Page 31: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/31.jpg)
31
![Page 32: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/32.jpg)
32
Modal
![Page 33: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/33.jpg)
33
Segue
Segue
Push 把目標 View Controller 加到 Navigation 堆疊最上層
Modal 顯示目標 View Controller
Custom 由使用者自定訂義 View Controller 之間的轉換
![Page 34: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/34.jpg)
34
Segue Push 與 Modal 的比較
Push較有結構,有 Navigation 堆疊幫忙回上一層 View
Controller有 Navigation Bar需要有 Navigation View Controller
Modal可任意帶出下一個 View Controller需自行定義 View Controller 之間的關係
![Page 35: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/35.jpg)
35
專案設定
![Page 36: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/36.jpg)
36
專案設定Xcode 5.1
iPhone Retina 4 inch, 64 bit
![Page 37: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/37.jpg)
37
![Page 38: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/38.jpg)
38
設定 framework加入
iAd.framework ( iAd 廣告 )
StoreKit.framework (In App Purchase, IAP, 內購 )
![Page 39: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/39.jpg)
39
專案資料夾
![Page 40: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/40.jpg)
40
變更預設 StoryBorad
![Page 41: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/41.jpg)
41
設定 App Icon
![Page 42: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/42.jpg)
42
設定 App 圖示與起始畫面
圖示
起始畫面
![Page 43: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/43.jpg)
43
設定 App 圖示與起始畫面
![Page 44: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/44.jpg)
44
設定起始畫面
![Page 45: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/45.jpg)
45
Tab Bar
![Page 46: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/46.jpg)
46
Tab Bar 範例
![Page 47: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/47.jpg)
47
Tab Bar 架構
![Page 48: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/48.jpg)
48
Tab Bar 設計
![Page 49: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/49.jpg)
49
![Page 50: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/50.jpg)
50
![Page 51: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/51.jpg)
51
新增 Tab Bar 的 View Controller
滑屬右鍵
![Page 52: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/52.jpg)
52
新增 Tab Bar 的 View Controller
![Page 53: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/53.jpg)
53
![Page 54: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/54.jpg)
54
Page Control
![Page 55: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/55.jpg)
55
Page Control
![Page 56: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/56.jpg)
56
使用到的元件
![Page 57: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/57.jpg)
57
![Page 58: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/58.jpg)
58
![Page 59: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/59.jpg)
59
![Page 60: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/60.jpg)
60
新增界面元件與 View Controller 的關係
選擇 Automatic, 讓 Xcode 自動幫你列出相關的 View Controller
![Page 61: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/61.jpg)
61
新增界面元件與 View Controller 的關係
建立 ScrollView Outlet
設定 ScrollView 的 delegate
![Page 62: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/62.jpg)
62
PageViewController.h
@interface PageViewController : UIViewController<UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
@end
PageViewController.m
@interface PageViewController ()
{
NSArray* imageArray;
}
@end
![Page 63: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/63.jpg)
63
PageViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// 建立圖片陣列 imageArray = [[NSArray alloc] initWithObjects:
@"wallpaper1.jpg",
@"wallpaper2.jpg",
@"wallpaper3.jpg",
@"wallpaper4.jpg",
@"wallpaper5.jpg",
@"wallpaper6.jpg",
@"wallpaper7.jpg",
@"wallpaper8.jpg",
nil];
![Page 64: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/64.jpg)
64
// 依序把圖片放到 Scroll View 的相對應位置 for (int i = 0; i < [imageArray count]; i++)
{
// 計算圖片大小及原點位置 CGRect frame;
frame.origin.x = self.scrollView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.scrollView.frame.size;
// 建立 UIImageView 物件存放圖片 UIImageView *imageView = [[UIImageView alloc] initWithFrame:frame];
imageView.image = [UIImage imageNamed:[imageArray objectAtIndex:i]];
[self.scrollView addSubview:imageView];
}
// 設定 Scroll View 的內容物大小 self.scrollView.contentSize=CGSizeMake(320*8, 460);
// 設定 Scroll View 委派物件為自己 self.scrollView.delegate = self;
}
![Page 65: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/65.jpg)
65
PageViewController.m
// 處理捲動事件- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// 取得目前顯示頁面的坐標 CGFloat pageWidth = self.scrollView.frame.size.width;
// 計算出頁碼 int page = floor((self.scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
// 設定頁碼 self.pageControl.currentPage = page;
}
UIScrollViewDelegate
![Page 66: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/66.jpg)
66
Table
![Page 67: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/67.jpg)
67
功能Table 資料的顯示
顯示詳細內容
移動
刪除
Navigation Bar
跨 View Controller 之間的資料傳遞
![Page 68: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/68.jpg)
68
![Page 69: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/69.jpg)
69
![Page 70: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/70.jpg)
70
![Page 71: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/71.jpg)
71
嵌入 Navigation Controller
![Page 72: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/72.jpg)
72
![Page 73: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/73.jpg)
73
Table ViewUITableViewDataSource
提供要顯示的資料
UITableViewDelegate處理 TableView 上的操作及事件
![Page 74: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/74.jpg)
74
Table View設定 dataSource 與 delegate
![Page 75: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/75.jpg)
75
![Page 76: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/76.jpg)
76
新增 Push Segue
![Page 77: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/77.jpg)
77
新增 Push Segue
![Page 78: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/78.jpg)
78
![Page 79: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/79.jpg)
79
設定自訂的 View Controller 類別
MyTableViewController繼承 UITableViewController
![Page 80: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/80.jpg)
80
MyTableViewController.m
@interface MyTableViewController ()
{
NSMutableArray *titles;
NSString *selectedString;
}
@end
![Page 81: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/81.jpg)
81
準備 Table 資料
![Page 82: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/82.jpg)
82
MyTableViewController.m
- (void)viewDidLoad
{
NSMutableArray *section;
[super viewDidLoad];
titles = [[NSMutableArray alloc] initWithCapacity:3];
// 第一個 Section 台南 section = [NSMutableArray arrayWithObjects:
@" 東區 ", @" 中西區 ", @" 北區 ", @" 南區 ", @" 永康區 ", @" 安平區 ", nil];
[titles addObject:section];
// 第二個 Section 高雄 section = [NSMutableArray arrayWithObjects:@" 三民 ", @" 前鎮 ", @" 苓雅 ", nil];
[titles addObject:section];
// 第三個 Section 屏東 section = [NSMutableArray arrayWithObjects:@" 三地門 ", @" 恆春 ", nil];
[titles addObject:section];
// 設定 Navigation Bar 的右邊按鈕為 Table View 的編輯按鈕 self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
![Page 83: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/83.jpg)
83
MyTableViewController.m// 設定 Section 數- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return titles.count;
}
// 設定 Section 內的列數- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return ((NSMutableArray*)[titles objectAtIndex:section]).count;
}
// 設定要顯示 cell- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 透過 Identifier: DefaultCell 取得 Cell UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:@"DefaultCell" forIndexPath:indexPath];
// 設定區域名稱 cell.textLabel.text =
[[titles objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
return cell;
}
UITableViewDataSource
UITableViewDataSource
UITableViewDataSource
![Page 84: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/84.jpg)
84
MyTableViewController.m
// 設定各 Section 的名稱- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *sectionName;
switch (section)
{
case 0:
sectionName = @" 台南 ";
break;
case 1:
sectionName = @" 高雄 ";
break;
default:
sectionName = @" 屏東 ";
break;
}
return sectionName;
}
UITableViewDataSource
![Page 85: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/85.jpg)
85
處理資料編輯 ( 刪除 )
![Page 86: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/86.jpg)
86
MyTableViewController.m// 設定是否允許編輯列 ( 刪除 )
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// 確定輯輯後狀態- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// 編輯的類型為『刪除』 if (editingStyle == UITableViewCellEditingStyleDelete) {
// 更新 Section 資料 NSMutableArray *section;
section = [titles objectAtIndex:indexPath.section];
[section removeObjectAtIndex:indexPath.row];
if (section.count == 0)
{ [titles removeObject:section]; }
// 更新表格狀態 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
UITableViewDelegate
UITableViewDelegate
![Page 87: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/87.jpg)
87
處理資料移動
![Page 88: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/88.jpg)
88
MyTableViewController.m
// 設定是否允許移動列- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// 移動列範圍判斷- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
// 不在同一個 Secion 的話,不允許移動 if( sourceIndexPath.section != proposedDestinationIndexPath.section )
return sourceIndexPath;
else
return proposedDestinationIndexPath;
}
UITableViewDelegate
UITableViewDelegate
![Page 89: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/89.jpg)
89
MyTableViewController.m
// 移動列- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
NSMutableArray* section;
// 判斷是否在同 Secion 內移動 if (fromIndexPath.section == toIndexPath.section)
{
NSString *fromString;
section = [titles objectAtIndex:fromIndexPath.section];
fromString = [section objectAtIndex:fromIndexPath.row];
// 把被移動的列從原本的 Secion 中刪除 [section removeObjectAtIndex:fromIndexPath.row];
// 把被移動的列加到新位置 [section insertObject:fromString atIndex:toIndexPath.row];
}
}
UITableViewDelegate
![Page 90: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/90.jpg)
90
處理資料選擇及跨 View Controller 之間的
資料傳遞
![Page 91: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/91.jpg)
91
MyTableViewController.m
// 處理按下列時的事件- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// 儲存該列的地名 selectedString = [[titles objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
// 執行 segue, 移動到 DetailViewController [self performSegueWithIdentifier:@"segueDetail" sender:self];
}
UITableViewDelegate
![Page 92: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/92.jpg)
92
MyTableViewController.m
// 準備處理 Segue- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// 判斷即將要執行的 Segue 是否為 segueDetail if ([[segue identifier] isEqualToString:@"segueDetail"])
{
// 取得目標 ViewController DetailViewController* detailViewController = [segue destinationViewController];
// 設定要顯示的地名 detailViewController.text = selectedString;
}
}
Segue
![Page 93: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/93.jpg)
93
DetailViewController.h
@interface DetailViewController : UIViewController
@property (weak, nonatomic) IBOutlet UILabel *textLabel;
@property (weak, nonatomic) NSString *text;
@end
DetailViewController.m
- (void)viewWillAppear:(BOOL)animated
{
// 設定標籤顯示地名 self.textLabel.text = self.text;
}
![Page 94: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/94.jpg)
94
Gesture手勢處理
![Page 95: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/95.jpg)
95
手勢類型Tap 點擊 Rotation 旋轉 Pinch 縮放
![Page 96: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/96.jpg)
96
手勢類型Swipe (掃動 )
固定方向上下左右
Pan (拖拉 )任何方向上下左右斜對角
![Page 97: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/97.jpg)
97
處理手勢的類別
![Page 98: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/98.jpg)
98
元件
![Page 99: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/99.jpg)
99
Tap 點擊不連續手勢類型
![Page 100: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/100.jpg)
100
狀態轉換
![Page 101: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/101.jpg)
101
Pinch 縮放連續手勢類型
![Page 102: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/102.jpg)
102
![Page 103: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/103.jpg)
103
UIGestureRecognizerState
UIGestureRecognizerStateBegan 開始UIGestureRecognizerStateChanged 改變中,連續型的才有UIGestureRecognizerStateEnded 結束UIGestureRecognizerStateCancelled 取消UIGestureRecognizerStateFailed 失敗UIGestureRecognizerStateRecognized
與 UIGestureRecognizerStateEnded 相同
![Page 104: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/104.jpg)
104
![Page 105: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/105.jpg)
105
取消 Auto Layout取消 Auto Layout
![Page 106: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/106.jpg)
106
Tap ( 點擊 )按一下跳至下一層
點擊次數 手指數
![Page 107: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/107.jpg)
107
Tap ( 點擊 )透過 Segue 直接連到 Swipe View Controller
![Page 108: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/108.jpg)
108
Swipe (掃動 )
透過左右滑動回到上一層或下一層
![Page 109: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/109.jpg)
109
連接 Tap 與 Swipe
加入 Bar Button Item 並建立 Segue
![Page 110: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/110.jpg)
110
選擇 Push
![Page 111: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/111.jpg)
111
建立的 Segue
![Page 112: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/112.jpg)
112
![Page 113: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/113.jpg)
113
Bar Item
設定名稱
![Page 114: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/114.jpg)
114
Swipe (掃動 )
右到左,透過 Segue 移動 左到右,透過 Sent Action 處理
![Page 115: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/115.jpg)
115
SwipeViewController.h
// 處理 Swipe Gesture 由左滑到右邊事件- (IBAction)handSwipeLeftToRight:(id)sender {
// pop 回上一層 view controller [self.navigationController popViewControllerAnimated:TRUE];
}
![Page 116: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/116.jpg)
116
Pan (拖拉 )
透過左右滑動回到上一層或下一層
![Page 117: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/117.jpg)
117
Pan (拖拉 )
由 Sent Action 處理,藉由 x 方向的移動速度,判斷左右
![Page 118: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/118.jpg)
118
PanViewController.h// 處理 Pan 手勢- (IBAction)handlePan:(id)sender {
UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer*)sender;
// 判斷是否為 pan 手勢結束狀態 if (panGesture.state == UIGestureRecognizerStateEnded)
{
// 取得移動速度 CGPoint velocity = [panGesture velocityInView:self.view];
// x > 0 為 左向右滑動, pop 回上一層 view controller if(velocity.x > 0)
{
[self.navigationController popViewControllerAnimated:TRUE];
}
// x < 為 右向左滑動 , 執行 segueLongPress // push 下一層 view controller (Long Press) else
{
[self performSegueWithIdentifier:@"segueLongPress" sender:self];
}
}
}
![Page 119: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/119.jpg)
119
Long Press (長按 )長按切換按鈕顏色
![Page 120: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/120.jpg)
120
Long Press (長按 )
由 Sent Action 處理
![Page 121: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/121.jpg)
121
LongViewController.h@interface LongPressViewController : UIViewController
// 處理長按手勢- (IBAction)handleLongPress:(id)sender;
// 中央按鈕@property (weak, nonatomic) IBOutlet UIButton *longPressButton;
@end
LongViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// 設定角落的圓弧半徑為 60 self.longPressButton.layer.cornerRadius = 60;
// 設定邊界線條寬度為 0,即隱藏邊界 self.longPressButton.layer.borderWidth = 0.0f;
}
![Page 122: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/122.jpg)
122
LongViewController.m
// 處理長按手勢- (IBAction)handleLongPress:(id)sender
{
// 型態轉換 UILongPressGestureRecognizer *longPressGesture = (UILongPressGestureRecognizer*)sender;
// 長按手勢開始 if (longPressGesture.state == UIGestureRecognizerStateBegan)
{
// 按鈕綠色變紅色 if ([self.longPressButton.backgroundColor isEqual:[UIColor greenColor]])
{
self.longPressButton.backgroundColor = [UIColor redColor];
}
// 按鈕紅色變綠色 else
{
self.longPressButton.backgroundColor = [UIColor greenColor];
}
}
}
![Page 123: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/123.jpg)
123
Pinch (縮放 )利用 Pinch 縮放圖片大
小
![Page 124: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/124.jpg)
124
UIImageView
![Page 125: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/125.jpg)
125
Pinch (縮放 )
由 Sent Action 處理
![Page 126: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/126.jpg)
126
PinchViewController.m
@interface PinchViewController ()
{
// 記錄上一次放大的倍數 CGFloat lastScale;
}
@end
![Page 127: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/127.jpg)
127
PinchViewController.m
// 處理 pinch 手勢- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer
{
float scale;
// 若為手勢剛開始狀態,設定 lastScale = 1.0 (無放大縮小 )
if (recognizer.state == UIGestureRecognizerStateBegan)
{
lastScale = 1.0;
return;
}
// 其它狀態 , 依 lastScale 的值來決定要放大多少 scale = 1 + (recognizer.scale-lastScale);
lastScale = recognizer.scale;
// 放大圖片 self.imageView.transform = CGAffineTransformScale(self.imageView.transform, scale, scale);
}
![Page 128: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/128.jpg)
128
Rotate (旋轉 ) 旋轉圖片
![Page 129: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/129.jpg)
129
Rotate (旋轉 )
由 Sent Action 處理
![Page 130: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/130.jpg)
130
RotationViewController.m
@interface RotationViewController ()
{
// 記錄上次旋轉角度 CGFloat lastRotation;
}
@end
![Page 131: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/131.jpg)
131
RotationViewController.m
// 處理 Rotation 手勢- (IBAction)handleRotation:(UIRotationGestureRecognizer *)sender
{
// 若為手勢剛開始狀態,設定 lastRotation = 0.0 (無旋轉 )
if([sender state] == UIGestureRecognizerStateBegan) {
lastRotation = 0.0;
return;
}
// 其它狀態 , 依 lastRotation 的值及來決定要旋轉幾度 CGFloat rotation = 0.0 - (lastRotation - [sender rotation]);
// 計算新的 transform CGAffineTransform currentTransform = self.imageView.transform;
CGAffineTransform newTransform = CGAffineTransformRotate(currentTransform,rotation);
// 設定新的 transform [self.imageView setTransform:newTransform];
// 更新 lastRotation 下次使用 lastRotation = [sender rotation];
}
![Page 132: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/132.jpg)
132
結合 Pan, Pinch, Rotation
可移動、縮放、旋轉圖片
![Page 133: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/133.jpg)
133
PinchRotationViewController.m
@interface PinchRotationViewController : UIViewController
// 處理 Rotation 手勢- (IBAction)handleRotation:(UIRotationGestureRecognizer *)sender;
// 處理 pinch 手勢- (IBAction)handlePinch:(UIPinchGestureRecognizer *)sender;
// 處理 Pan 手勢- (IBAction)handlePan:(UIPanGestureRecognizer *)sender;
// UIImageView, 圖片 UI 物件@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
![Page 134: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/134.jpg)
134
PinchRotationViewController.m
// 處理 pan 手勢- (IBAction)handlePan:(UIPanGestureRecognizer *)sender
{
// 若是 pan 手勢進行中或結束時,更新圖片位置 if ((sender.state == UIGestureRecognizerStateChanged) ||
(sender.state == UIGestureRecognizerStateEnded)) {
// 取得手勢位置 CGPoint location = [sender locationInView:self.view];
// 設定圖片中心 self.imageView.center = location;
}
}
![Page 135: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/135.jpg)
135
Collection
![Page 136: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/136.jpg)
136
UICollectionViewController
![Page 137: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/137.jpg)
137
Collection 設計
![Page 138: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/138.jpg)
138
Collection Story Board
![Page 139: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/139.jpg)
139
Collection View
勾選 Header把 UIImageView拉到 Cell
![Page 140: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/140.jpg)
140
![Page 141: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/141.jpg)
141
設定 Cell 及 Header 大小
![Page 142: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/142.jpg)
142
嵌入 Navigation View Controller
Embedded View Controller
![Page 143: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/143.jpg)
143
加入 Full Image View Controller
![Page 144: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/144.jpg)
144
建立 Section Header 的類別及識別
建立 Cell 的類別及識別
建立 Full Image View Controller 類別
![Page 145: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/145.jpg)
145
Collection Reusable View
設定 Identifier
![Page 146: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/146.jpg)
146
Collection Reusable View
設定 Identifier
![Page 147: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/147.jpg)
147
Full Image View Controller UIImageView 設定
![Page 148: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/148.jpg)
148
MyCollectionViewCell.h
@interface MyCollectionViewCell : UICollectionViewCell
@property (strong, nonatomic) IBOutlet UIImageView *imageView;
@end
MySupplementaryView.h
@interface MySupplementaryView : UICollectionReusableView
@property (weak, nonatomic) IBOutlet UILabel *headerLabel;
@end
![Page 149: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/149.jpg)
149
FullImageViewController.h
@interface FullImageViewController : UIViewController
// 要顯示的 UIImageView@property (weak, nonatomic) IBOutlet UIImageView *imageView;
// 暫存的圖片@property (strong, nonatomic) UIImage *image;
@end
FullImageViewController.m
- (void)viewWillAppear:(BOOL)animated
{
// 設定 imageView 的圖片 self.imageView.image = self.image;
}
![Page 150: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/150.jpg)
150
MyCollectionViewController.m
#import "MySupplementaryView.h"
#import "FullImageViewController.h"
@interface MyCollectionViewController ()
{
// 選擇的圖片 UIImage *selectedImage;
// Secion 0 圖片名稱字串陣列 NSMutableArray *images0;
// Secion 1 圖片名稱字串陣列 NSMutableArray *images1;
}
@end
![Page 151: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/151.jpg)
151
MyCollectionViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// 建立 2 個 Section 的圖片名稱字串陣列 images0 = [@[@"fish1.png",
@"fish2.png",
@"fish3.png",] mutableCopy];
images1 = [@[@"fish3.png",
@"fish2.png",
@"fish1.png",] mutableCopy];
}
- (void)viewWillAppear:(BOOL)animated
{
// 隱藏 Navigation Bar [[self navigationController] setNavigationBarHidden:YES animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
// 顯示 Navigation Bar [[self navigationController] setNavigationBarHidden:NO animated:YES];
}
![Page 152: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/152.jpg)
152
MyCollectionViewController.m
// 回傳 Section 個數 , 2 個 Section- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 2;
}
// 回傳 Section 裡的項目個數- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
if (section == 0)
return images0.count;
return images1.count;
}
UICollectionViewDataSource
UICollectionViewDataSource
![Page 153: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/153.jpg)
153
MyCollectionViewController.m
// 設定各 Section 的名稱-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
MySupplementaryView *header = nil;
// 判斷是 header 還是 footer if ([kind isEqual:UICollectionElementKindSectionHeader])
{
// 取得 header header = [collectionView dequeueReusableSupplementaryViewOfKind:kind
withReuseIdentifier:@"MyHeader"
forIndexPath:indexPath];
// 設定標題 if (indexPath.section == 0)
header.headerLabel.text = @"Fish 0 Gallery";
else
header.headerLabel.text = @"Fish 1 Gallery";
}
return header;
}
UICollectionViewDataSource
![Page 154: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/154.jpg)
154
MyCollectionViewController.m
// 設定要顯示 cell- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UIImage *image;
// 取得 cell MyCollectionViewCell *myCell = [collectionView
dequeueReusableCellWithReuseIdentifier:@"MyCell"
forIndexPath:indexPath];
// 依 cell 位置,建立圖片 if (indexPath.section == 0)
image = [UIImage imageNamed:images0[indexPath.row]];
else
image = [UIImage imageNamed:images1[indexPath.row]];
// 設定 cell 要顯示的圖片 myCell.imageView.image = image;
return myCell;
}
UICollectionViewDataSource
![Page 155: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/155.jpg)
155
MyCollectionViewController.m
// 處理選擇的項目- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
// 取得選擇的圖片 if (indexPath.section == 0)
selectedImage = [UIImage imageNamed:images0[indexPath.row]];
else
selectedImage = [UIImage imageNamed:images1[indexPath.row]];
// 執行 Segue: pushShowFullImage [self performSegueWithIdentifier: @"pushShowFullImage" sender: self];
}
// 準備執行 Segue- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// 判斷要執行的 Segue 是否為 pushShowFullImage if ([[segue identifier] isEqualToString:@"pushShowFullImage"])
{
// 取得目標 View Controller FullImageViewController *fvc = [segue destinationViewController];
// 設定 FullImageViewController 要顯示的圖片 fvc.image = selectedImage;
}
}
UICollectionViewDelegate
![Page 156: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/156.jpg)
156
Timer and Thread
![Page 157: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/157.jpg)
157
Clock
使用 NSTimer 及 NSThread 實作時鐘
利用 UISwitch 切換開關
使用 PickerView 選擇時間格式
![Page 158: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/158.jpg)
158
設定 UISwitch 的 Value Changed 事件
![Page 159: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/159.jpg)
159
設定 Picker View 的 delegate 及 datasource
![Page 160: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/160.jpg)
160
NSThread 限制透過 NSThread 建立出來的執行緒與控制界面的主執行緒是不一樣的
NSTimer 建立出來的計時器可以變更界面內容
NSThread 逼立出來的執行緒不能變更界面內容需要回到主執行緒才能變更
performSelectorOnMainThread:withObject:waitUntilDone:
performSelectorOnMainThread:withObject:waitUntilDone:modes:
![Page 161: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/161.jpg)
161
取得介面元件的另一個方法介面元件都有一個 Tag
不一定要透過 Story Board 連接 View Controller 與介面元件之間的關係
View Controller 可以透過 Tag 在程式執行期間,動態取得介面元件
[self.view viewWithTag:1]
![Page 162: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/162.jpg)
162
設定 Tag
![Page 163: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/163.jpg)
163
ClockViewController.m
@interface ClockViewController ()
{
// UI 界面元件 UILabel *clockLabel;
UISwitch *timerSwitch;
UISwitch *threadSwitch;
// 儲存 date format 字串的陣列 NSArray *dateFormat;
// 計時器 NSTimer *clockTimer;
// 執行緒 NSThread *clockThread;
// 執行緒生命判斷標籤 BOOL isThreadAlive;
// 目前使用的 date format NSDateFormatter *dateFormatter;
}
@end
![Page 164: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/164.jpg)
164
ClockViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
clockTimer = nil;
clockThread = nil;
isThreadAlive = FALSE;
// 透過 tag 取得 UI 介面元件 clockLabel = (UILabel*)[self.view viewWithTag:1];
timerSwitch = (UISwitch*)[self.view viewWithTag:2];
threadSwitch = (UISwitch*)[self.view viewWithTag:3];
// 設定 dateFormat 陣列 dateFormat = [NSArray arrayWithObjects:@"HH:mm:ss", @"ss", @"yyyy-MM-dd HH:mm:ss", @"MM/dd HH:mm:ss", nil];
// 設定預設 date format 為 dateFormat 字串陣列裡的第 0 個 dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:[dateFormat objectAtIndex:0]];
}
![Page 165: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/165.jpg)
165
ClockViewController.m
// 設定 PickerView 的元件個數- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
return 1;
}
// 設定 PickerView 元件裡的列數- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return dateFormat.count;
}
// 設定 PickerView 列要顯示的字串- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return [dateFormat objectAtIndex:row];
}
// 處理選擇 PickerViw 列的事件- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
// 設定新的 date format [dateFormatter setDateFormat:[dateFormat objectAtIndex:row]];
}
UIPickerViewDataSource
UIPickerViewDataSource
UIPickerViewDataSource
UIPickerViewDelegate
![Page 166: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/166.jpg)
166
ClockViewController.m
// 啟動 Timer- (void)startTimer
{
if (clockTimer == nil)
{
clockTimer =
[NSTimer scheduledTimerWithTimeInterval:1 target:self
selector:@selector(clockTimeout) userInfo:nil repeats:YES];
}
}
// 停止 Timer- (void)stopTimer
{
if (clockTimer != nil)
{
[clockTimer invalidate];
clockTimer = nil;
}
}
// 處理 timeout 事件 , 更新要顯示的時間- (void)clockTimeout
{
// 依目前的 dateFormatter 格式顯示時間 clockLabel.text = [dateFormatter stringFromDate:[NSDate date]];
}
![Page 167: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/167.jpg)
167
ClockViewController.m
// 啟動執行緒- (void)startThread
{
if (clockThread == nil)
{
clockThread = [[NSThread alloc] initWithTarget:self
selector:@selector(threadMain)
object:nil];
isThreadAlive = TRUE;
[clockThread start]; // Actually create the thread
}
}
// 停止執行緒- (void)stopThread
{
if (clockThread != nil)
{
clockThread = nil;
isThreadAlive = FALSE;
}
}
![Page 168: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/168.jpg)
168
ClockViewController.m
// 執行緒本體- (void)threadMain
{
while(isThreadAlive)
{
// 通知 Main Thread 執行 clockTimeout [self performSelectorOnMainThread:@selector(clockTimeout) withObject:nil waitUntilDone:FALSE];
// 睡 1 秒 sleep(1);
}
}
![Page 169: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/169.jpg)
169
ClockViewController.m// 處理 UISwitch value changed 事件- (IBAction)handleSwitchValueChanged:(id)sender { // 若是 timerSwitch // 1. 停止 thread 執行 // 2. 依 timerSwitch 狀態決定要執行或是停止 timer if (sender == timerSwitch) { [self stopThread]; threadSwitch.on = FALSE; if (timerSwitch.on) [self startTimer]; else [self stopTimer]; } // 若是 threadSwitch // 1. 停止 timer 執行 // 2. 依 threadSwitch 狀態決定要執行或是停止 thread else { [self stopTimer]; timerSwitch.on = FALSE; if (threadSwitch.on) [self startThread]; else [self stopThread]; }}
UISwitch
![Page 170: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/170.jpg)
170
iAd 與 IAP
![Page 171: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/171.jpg)
171
iAd 與 IAPSimulator 不能測試 IAP
需要購買 iOS Developer Program
賺錢全靠他們倆
建立 IAP 項目
建立測試人員不然你要一直花自己的錢去測試 IAP 嗎?
![Page 172: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/172.jpg)
172
iOS Developer Program
![Page 173: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/173.jpg)
173
建立新的 App
iTune Connecthttps://itunesconnect.apple.com
![Page 174: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/174.jpg)
174
![Page 175: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/175.jpg)
175
![Page 176: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/176.jpg)
176
上架時間及價格
![Page 177: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/177.jpg)
177
Version Information
![Page 178: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/178.jpg)
178
![Page 179: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/179.jpg)
179
App 資料
![Page 180: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/180.jpg)
180
聯絡資訊
![Page 181: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/181.jpg)
181
上傳 App 圖片
![Page 182: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/182.jpg)
182
新增 IAP
![Page 183: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/183.jpg)
183
新增 IAP
![Page 184: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/184.jpg)
184
選擇單賣的方式
![Page 185: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/185.jpg)
185
IAP 項目名稱及價格
![Page 186: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/186.jpg)
186
設定 IAP 項目語言
![Page 187: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/187.jpg)
187
新增測試人員
![Page 188: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/188.jpg)
188
新增測試人員
![Page 189: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/189.jpg)
189
新增測試人員Email 可以亂填沒關係,不會認證,只會當 ID 用
![Page 190: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/190.jpg)
190
問題如何取得 IAP 項目資料?
如何購買 IAP 項目?
如何取得已購買項目?
如何記録已購買項目?
![Page 191: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/191.jpg)
iAd 廣告
![Page 192: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/192.jpg)
192
![Page 193: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/193.jpg)
193
設定按鈕 Touch Up Inside 事件處理
![Page 194: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/194.jpg)
194
iAd 及 IAP framework加入 iAd.framework 及 StoreKit.framework
![Page 195: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/195.jpg)
195
處理 IAP項目 說明
取得 IAP 項目
透過 SKProductRequest 處理設定 SKProductRequestDelegate實作 productsRequest:didReceiveResponse:啟動 SKProductRequest 物件
購買 IAP 項目
透過 [SKPaymentQueue defaultQueue] 處理實作 paymentQueue:updatedTransactions:設定 addTransactionObserver[[SKPaymentQueue defaultQueue] addPayment:payment]
恢復 IAp 項目
透過 [SKPaymentQueue defaultQueue] 處理設定 addTransactionObserver實作 paymentQueue:updatedTransactions:[[SKPaymentQueue defaultQueue] restoreCompletedTransactions]
![Page 196: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/196.jpg)
196
取得 IAP 項目清單
讀取 User Default更新 IAP 項目界面
啟動 App
處理 SKPaymentQueue
交易事件
購買
SKPaymentQueue addPayment
記錄購買項目在 User Default
處理 SKPaymentQueue
交易事件
恢復購買
SKPaymentQueue restoreCompleted
Transactions
記錄購買項目在 User Default
![Page 197: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/197.jpg)
197
使用者設定檔 使用 NSUserDefaults 類別 儲存在
<Application_Home>/Library/Preferences/<BundleId>.plist 以 Key, Value 的方式儲存 Value 資料型態
Integer, float, double, string, dictionary, object
// 取得 NSUserDefault 物件NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 設定 (key, value)[defaults setBool:FALSE forKey:@"key"];
// 依 key 取得 valueBOOL value = [defaults boolForKey:@"key"];
![Page 198: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/198.jpg)
198
IADViewController.h
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
#import <StoreKit/StoreKit.h>
@interface IADViewController : UIViewController<SKProductsRequestDelegate, SKPaymentTransactionObserver>
// IAP 項目 UI 介面元件@property (weak, nonatomic) IBOutlet UILabel *productIdentifierLabel;
@property (weak, nonatomic) IBOutlet UILabel *productPriceLabel;
@property (weak, nonatomic) IBOutlet UILabel *productDescriptionLabel;
@property (weak, nonatomic) IBOutlet UILabel *productTitleLabel;
@property (weak, nonatomic) IBOutlet UILabel *purchaseStatus;
// 廣告@property (weak, nonatomic) IBOutlet ADBannerView *adBanner;
// 按下購買- (IBAction)pressPurchase:(id)sender;
// 按下恢復購買狀態- (IBAction)pressRestore:(id)sender;
@end
![Page 199: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/199.jpg)
199
IADViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// 清除購買記錄 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:FALSE forKey:PRODUCT_IDENTIFIER];
// 更新購買狀態 [self updatePurchaseStatus];
// 下載 IAP Product [self retrieveIapProduct];
// 註冊 SKPaymentQueue 觀察者 [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
![Page 200: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/200.jpg)
200
IADViewController.m
// 按下購買按鈕- (IBAction)pressPurchase:(id)sender {
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
// 按下恢復購買按鈕- (IBAction)pressRestore:(id)sender {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
// 設定 IAP 項目已購買- (void)updateIAPPurchased:(NSString*)key
{
// 取得 UserDefault 物件 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// 設定 com.comig.iphoneui.iap.noad 的值為 TRUE [defaults setBool:TRUE forKey:key];
[self updatePurchaseStatus];
}
![Page 201: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/201.jpg)
201
IADViewController.m
// 更新介面購買狀態-(void)updatePurchaseStatus
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults boolForKey:PRODUCT_IDENTIFIER])
{
self.purchaseStatus.text = @"已購買 ";
// 隱藏廣告 self.adBanner.hidden = YES;
}
else
{
self.purchaseStatus.text = @"尚未購買 ";
// 顯示廣告 self.adBanner.hidden = NO;
}
}
![Page 202: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/202.jpg)
202
IADViewController.m
// 開始下載 IAP 項目-(void)retrieveIapProduct {
// 設定要下載的項目 _productIdentifiers = [NSSet setWithObjects:PRODUCT_IDENTIFIER, nil];
// 建立 SKProductRequest 物件 _productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:_productIdentifiers];
// 設定委派 _productsRequest.delegate = self;
[_productsRequest start];
}
// 處理下載 IAP 項目- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
// 取得第一個 IAP 項目 , 注意 , response.products 是陣列 product = [response.products objectAtIndex:0];
self.productTitleLabel.text = product.localizedTitle;
self.productIdentifierLabel.text = product.productIdentifier;
self.productPriceLabel.text = product.localizedPrice;
self.productDescriptionLabel.text = product.localizedDescription;
}
![Page 203: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/203.jpg)
203
處理 SKPaymentQueue 交易事件
項目 說明SKPaymentTransactionStatePurchasing 購買處理中SKPaymentTransactionStatePurchased 購買成功SKPaymentTransactionStateFailed 購買 /恢復失敗SKPaymentTransactionStateRestored 恢復成功
enum SKPaymentTransactionState
![Page 204: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/204.jpg)
204
IADViewController.m
// 處理 SKPaymentQueue 交易事件- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self alertMessage:@" 購買成功 "];
[self updateIAPPurchased:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self alertMessage:@" 購買失敗 "];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self alertMessage:@"恢復已購買成功 "];
[self updateIAPPurchased:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
default:
break;
}
};
}
![Page 205: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/205.jpg)
205
整合所有範例
![Page 206: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/206.jpg)
206
統整 Story Board
Main
Tab BarPage
ControlTable
Collection
Clock iAd
![Page 207: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/207.jpg)
207
版面設計
![Page 208: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/208.jpg)
208
設定 StoryBoard ID View Controller 需要設定
StoryBoardID
因為 Main StoryBoard 已經包了一個 Navigation View Controller ,所以不能以 Navigation View Controller 當開頭
![Page 209: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/209.jpg)
209
ViewController.m@interface ViewController (){ UIStoryboard *tabBarStoryboard; UIStoryboard *pageControlStoryboard; UIStoryboard *tableStoryboard; UIStoryboard *touchStoryboard; UIStoryboard *collectionStoryboard; UIStoryboard *clockStoryboard; UIStoryboard *iADStoryboard;}@end
@implementation ViewController
- (void)viewDidLoad{ [super viewDidLoad]; // 建立 Story Bard 物件 tabBarStoryboard = [UIStoryboard storyboardWithName:@"TabBar" bundle:nil]; pageControlStoryboard = [UIStoryboard storyboardWithName:@"PageControl" bundle:nil]; tableStoryboard = [UIStoryboard storyboardWithName:@"Table" bundle:nil]; touchStoryboard = [UIStoryboard storyboardWithName:@"Touch" bundle:nil]; collectionStoryboard = [UIStoryboard storyboardWithName:@"Collection" bundle:nil]; clockStoryboard = [UIStoryboard storyboardWithName:@"Clock" bundle:nil]; iADStoryboard = [UIStoryboard storyboardWithName:@"IAD" bundle:nil];}
![Page 210: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/210.jpg)
210
ViewController.m
// 顯示 Tab Bar 範例- (IBAction)pressTabBar:(id)sender {
UIViewController *vc = [tabBarStoryboard instantiateViewControllerWithIdentifier:@"TabBarViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
// 顯示 Page Control 範例- (IBAction)pressPageControl:(id)sender {
UIViewController *vc = [pageControlStoryboard instantiateViewControllerWithIdentifier:@"PageViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
// 顯示 Table 範例- (IBAction)pressTable:(id)sender {
UIViewController *vc = [tableStoryboard instantiateViewControllerWithIdentifier:@"TableViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
// 顯示 Touch 範例- (IBAction)pressTouch:(id)sender {
UIViewController *vc = [touchStoryboard instantiateViewControllerWithIdentifier:@"TapViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
![Page 211: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/211.jpg)
211
ViewController.m
// 顯示 Collection 範例- (IBAction)pressCollection:(id)sender {
UIViewController *vc = [collectionStoryboard instantiateViewControllerWithIdentifier:@"CollectionViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
// 顯示 Timer 與 Thread 範例- (IBAction)pressTimerAndThread:(id)sender {
UIViewController *vc = [clockStoryboard instantiateViewControllerWithIdentifier:@"ClockViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
// 顯示 iAd 與 IAP 範例- (IBAction)pressIADAndIAP:(id)sender {
UIViewController *vc = [iADStoryboard instantiateViewControllerWithIdentifier:@"IADViewController"];
[self.navigationController pushViewController:vc animated:TRUE];
}
![Page 212: iOS App 開發 -- Storybard 基礎練習、APP 上架、IAP](https://reader033.vdocuments.net/reader033/viewer/2022061605/554f5a3cb4c905524c8b545b/html5/thumbnails/212.jpg)
212
謝謝