Posted on

IOS6與IOS7的不同處整理

1. View會和狀態列重疊(壓在其之下)

2. 按鈕沒有邊框

3. tab bar變為半透明

螢幕快照 2013-10-06 下午7.18.46

4. 狀態列的顏色不會與導覽列分開

5. tintColor的影響顏色範圍改變,導覽列與tab bar的顏色不會受到tint Color的影響
ps:經測試,在ios7對於Segmented Control的tintColor,如果使用Global tint,一開始時的框線顏色不會變,但點過後顏色會改變。此時必須去各別設定該元件的tint color才會完整變更顏色

6. tab bar的圖示有按下狀態及非按下狀態螢幕快照 2013-10-06 下午7.24.37

7. 文字樣式明顯不同(即使是相同的設定)

8. 預設會是全螢幕畫面

9. 狀態列的高度不再會影響到整體畫面高度(整體畫面會在狀態列之下)

10. NSBundle Class改變Chia-Hsin分享
ios7的pathForResource (NSBundle Class)方法有調整過,
例,Local/iphone/page/01/01.jpg,
ios7之前用:
folder = Local
fileName = iphone/page/01/01.jpg

參數帶入function裡面是OK的~

但現在ios7要改成:
folder = Local/iphone/page/01
fileName = 01.jpg

11. iOS 7 的UITextField會預先給值,iOS 6 不會
if(![username.text isKindOfClass:[NSString Class]]){
NSLog(@”你沒有輸入帳號”);
}
因此這邊在ios7,若使用者沒有輸入任何文字,還是會成立。

12. viewdidload執行順序改變(建議:直接用@property帶值過去,然後在viewWillAppear裡去執行動作)
IOS6的code
ViewController *view1;
[[self navigationController] pushViewController:view1animated:TRUE];
[view1 method];

iOS7以前, 順序為view1被push出來以後會先執行viewdidload候才會執行[view1 method]
而iOS7後變成會先執行[view1 method]才執行viewdidload
目前在push前加viewController.view.hidden = NO;可以解決我現在的問題

Micheal老師建議:本來就是在 View Controller 裡面,去判斷的,不是在你寫的 pushViewController 那段程式碼。你寫的這段的地方,view controller 的 view還沒呈現,所以 view 也沒有值。之前會有值,只是剛好。我們不能決定什麼時候 ViewController 產生 view 的值,我們只知道,在 viewDidLoad 會有 view 的物件,在 viewDidAppear,view 的 frame 會被決定(如果是 storyboard 上的 view 的話)。 再來收到 delegate 回傳值要反應的 view 是 ViewController 的 view 為什不把接收者,設定為 ViewController。

附註:狀態列改變的相關議題

(一)讓上方留狀態列的位置

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
[application setStatusBarStyle:UIStatusBarStyleLightContent];
self.window.clipsToBounds =YES;
self.window.frame =  CGRectMake(0,20,self.window.frame.size.width,self.window.frame.size.height-20);</code></strong>
<strong><code>
self.window.bounds = CGRectMake(0, 20, self.window.frame.size.width, self.window.frame.size.height);
}
return YES;
}

(二)座標計算不將狀態列的地方算入

if([[[UIDevice currentDevice] systemVersion] floatValue] &gt;= 7.0)
{
self.edgesForExtendedLayout = UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars = NO;
self.modalPresentationCapturesStatusBarAppearance = NO;

self.navigationController.navigationBar.translucent = NO;
self.tabBarController.tabBar.translucent = NO;

self.navigationController.navigationBar.barTintColor =[UIColor grayColor];
self.tabBarController.tabBar.barTintColor =[UIColor grayColor];
}

(三)不顯示狀態列

  1. 至project setting=>General=>Deployment Info的地方勾選Hide during application launch
  2. 在ViewController加上
    -(BOOL) prefersStatusBarHidden{
    return YES;
    }

(四)改變狀態列顯示樣子(將字體顯示為白色)
-(UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}

相關資料來源:

Posted on

UIWebView練習作業

作業目標:http://www.youtube.com/watch?v=azplcMbuoQ4&feature=youtu.be
練習檔案:homework0831

先介紹一下UIWebView

UIWebView的資料來源主要有三者:

  1. 本機上的HTML字串
  2. 本機上的資料 + MIME type
  3. 遠端的URL

這邊有中文的介紹:http://www.inside.com.tw/2010/03/14/cs193p-lecture-12

作業練習開始

首先我先拉出storyboard
螢幕快照 2013-09-04 下午11.30.34

然後我練習使用程式去產生UINavigationController,這是在AppDelegate.m裡

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

UINavigationController * navi = [UINavigationController new];
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
UIViewController * controller = [storyboard instantiateViewControllerWithIdentifier:@"pink"];
[navi pushViewController:controller animated:YES];
self.window.rootViewController = navi;
return YES;
}

然後ViewController.h裡在viewDidLoad時初始化一些頁面數值

- (void)viewDidLoad
{
[super viewDidLoad];
[self.inputURL setAdjustsFontSizeToFitWidth:YES];//設定下方網址列的大小會自動resize
self.title = @"填入網址";
self.inputURL.text = @"http://www.apple.com";
}

實作按鈕按下時的動作

- (IBAction)goWeb:(id)sender {
//取出webview的view
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
WebViewController * webView = [storyboard instantiateViewControllerWithIdentifier:@"webView"];
webView.urlString = self.inputURL.text;
webView.delegate = self;
[self.inputURL resignFirstResponder];//收起鍵盤
[self.navigationController pushViewController:webView animated:YES];
}

接下來做WebViewController.m裡的內容,
在這邊我弄了一個下載進度的物件叫作UIActivityIndicatorView
這部份首先要先在- (void)viewDidLoad裡初始化載入效果

//加入載入特效
aciv = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; colorView=[[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

[colorView setBackgroundColor:[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:0.4]];

然後加上下面兩段程式碼

//載入特效
- (void)addLoading{
aciv.activityIndicatorViewStyle=UIActivityIndicatorViewStyleWhite; aciv.center=CGPointMake(self.view.frame.size.width/2,self.view.frame.size.height/2-10);
[self.view addSubview:colorView];
[aciv startAnimating];
[self.view addSubview:aciv];
}

- (void)removeLoading{
[colorView removeFromSuperview];
[aciv stopAnimating];
[aciv removeFromSuperview];
}

然後在實做UIWebView的Delegate去設定載入畫面
[UIApplication sharedApplication].networkActivityIndicatorVisible這個是在上方指示列的圈圈圖示

- (void)webViewDidStartLoad:(UIWebView *)webView
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[self addLoading];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
[self removeLoading];
}

遇到問題(高手幫忙看3Q)

然後其實接下來這一點問題我弄了非常久~ 就是,在從上一頁傳來網址時,
也會執行到
-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
所以會必須去判斷現在是使用者點擊的link,或是是從上頁傳來的指令

為了這個需求,我用了幾個方式,但都有問題,包括現在的其實也有,但因為實在搞太久~
所以就先這樣@@等老師公布他的作法後再來補修正

我總用試了這些方式

  1. 使用一個BOOL去判別,只有第一次loading頁面時不做跳頁呼叫,然後當webViewDidFinishLoad時將BOOL改為NO
    問題點:有某些頁面會有js檔案,當頁面全部載入完成後,執行js後,就又會再第二次讀取網路資料,此時頁面就會莫名的突然回到第一頁然後顯示網址。(例如:我的部落格http://claire-chang.com)
  2. 使用NSConnection(http://stackoverflow.com/questions/9475768/loading-nsdata-into-a-uiwebview)
    這是一個異想天開(?)的想法,我想說,第一次loading時用NSConnection去讀html,然後用loadHTMLString:baseURL:來顯示在畫面上。結果html成功的載下來了,但是不知為何WebView呈現一片空白。
    在網路上查了一下,似乎是因為<scripe>標籤的關係(不確定)
  3. 現在版本的方式:偵測使用者點擊事件,當使用者點擊時,判斷現在是使用者點擊事件,下一次的網頁讀取便會跳頁。
    不過這種方式也有問題,同第一點,不過狀況會變成若頁面尚未載入完,使用者就點擊非link的地方時,js呼叫讀取網頁時,就會跳回第一頁,然後網址顯示為js呼叫的頁面網址= =

也是因為這樣子,我才會去製作下載畫面,想說在未loading完前不讓使用者按下WebView,
結果因為畫面會先loading完成、然後才自動因js而去呼叫另一個網址。
中間還是會有使用者可以點擊畫面的時間點。

我總覺得UIWebView應該有個東西是可以直接判別是否是使用者呼叫的,
觀察過dataDetectorTypes的值,卻似乎不是~orz
有知道的人麻煩幫解惑吧~3Q!
ps:若想試有js會在loading完成後再次loading的狀況,請把網址設為我的部落格http://claire-chang.com

因為我是用第三種方式,所以我對WebView增加了碰觸事件的偵聽
首先在- (void)viewDidLoad設定碰觸事件監聽

//設定碰觸事件
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleTapFrom:)];
longPress.minimumPressDuration = .001;
longPress.delegate = self;
[self.web addGestureRecognizer:longPress];
[self.web addGestureRecognizer:test];

然後要實作UIGestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

上面這個一定要加,這樣子handleTapFrom:才會被呼叫到
然後當按到時將bool值改成YES

-(void)handleTapFrom :(UITapGestureRecognizer *)sender{
    NSLog(@"handleTapFrom:%d",sender.state);
    isClick = YES;
}

當讀取網頁且isClick是YES時就跳回第一頁

-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    if(isClick){
        [self.delegate userClickWebLink:request.URL.absoluteString];
        return NO;
    }
    return YES;
}

這時再回去ViewController.m增加接收的方法

-(void) userClickWebLink: (NSString *) url{
    [self.navigationController popViewControllerAnimated:YES];
    self.title = @"點選網址";
    self.webUrl.text = url;
}
Posted on

hasEventListener()與willTrigger()區別

承上一篇介紹Event事件流的基本概念
AS3的事件傳遞機制(Event、dispatchEvent及addEventListener)
再來介紹相關的檢查函數。

hasEventListener()方法:
檢查 EventDispatcher 對象是否為有註冊這個事件的監聽器。
willTrigger()方法:
檢查是否用此 EventDispatcher 對象或其父容器是否有註冊這個事件的監聽器

hasEventListener() 與 willTrigger()的區別是:hasEventListener() 只檢查它所屬的對象
而 willTrigger() 檢查對象以及對象的父容器是否有註冊這個事件(不管capture的值)。

假如像下面這樣的物件階層來說

efAzj

今天我們對stage註冊事件

stage.addEventListener(MouseEvent.CLICK, stageClick);

則檢查box的willTrigger及hasEventListener

trace(box.willTrigger(MouseEvent.CLICK));
trace(box.hasEventListener(MouseEvent.CLICK));

的結果為
true
false

而若我們對box註冊事件

box.addEventListener(MouseEvent.CLICK, stageClick);

則檢查box的willTrigger及hasEventListener

trace(box.willTrigger(MouseEvent.CLICK));
trace(box.hasEventListener(MouseEvent.CLICK));

的結果為
true
true

而若我們對box註冊事件

box.addEventListener(MouseEvent.CLICK, stageClick);

則檢查stage的willTrigger及hasEventListener

trace(stage.willTrigger(MouseEvent.CLICK));
trace(stage.hasEventListener(MouseEvent.CLICK));

的結果為
false
false
代表box不在stage完整可能有的事件流的動線裡。

Posted on 1 Comment

Flex記憶體監控程式-Profile

Flash Builder裡面大家最廣泛用來監控記憶體使用狀況的工具應該就是Profile了
Profile位於執行鈕的右邊,如下圖:
2013-08-27_160230

這邊提供幾個可能你按下Profile結果卻無法正常執行的一些討論串
Flex Builder – Profiler – Can’t run profiler

這個討論串有給一些嘗試的除錯方法

  1. 更改port號碼
  2. 是否SWF本身有錯誤?PreloadSwf 應位於本地端
  3. 換一個瀏覽器! (Preferences/General/Web Browser)也許 Firefox無法支援(我有遇過開不了的問題,我自己是換一個瀏覽器就解決了PS: 原本是firefox換成IE)
  4. 指定的瀏覽器是否有安裝debug版本Player?
  5. 設定mm.cfg檔案的值 (請看這邊 http://jpauclair.net/2010/02/10/mmcfg-treasure/)

設定開啟瀏覽器的地方在Window>Preference>General>Web Brower
2013-08-27_161918

Profile的設定則在這邊(兩個enable的選項要記得勾選),port之類的更改也在此區更改
2013-08-27_162200

接下來,請選擇工作區

2013-08-27_162621

切到Profile模式之後,如果我們想觀看外部SWF的記憶體使用狀況,
可以按Profile>Profile External Application,一樣可以進入觀查模式
2013-08-27_162737

2013-08-27_163808

在執行Profile時,左上的欄位可選擇要觀察的物件,
在profile視窗裡,可以設定中斷點,然後用右上的去控制繼續執行、暫停等等
2013-08-27_164013

在Memory Usage可以顯示現在所耗的記憶體的狀況
2013-08-27_163936

Live Objects裡觀察現在在記憶體內的物件以及它的值
2013-08-27_163948

如果還想開其他的頁籤,也可以由Window=>Show View>Other>Flash Prifile去叫出其他觀察視窗。
2013-08-27_164904

Posted on

我的第一款APP企劃

畫面繪製工具: Balsamiq Mockups
參考規範: iPhone-HIG(人機界面指南)
企劃圖片下載: APP開發

名稱:94愛吃
類別:生產力應用程式
分類:美食
簡介:
想揪三五好友去吃某間超好吃的餐廳時,卻總是忘了餐廳的詳細資訊嗎?
想找一間適合談公事的餐廳,卻又一時想不起究竟那裡適合嗎?

這一款APP可以幫助你記錄曾吃過的餐廳,以及想吃卻還未去吃的餐廳。
也可以以地圖的方式,去告訴你這附近有那些你吃過的餐廳可以去吃。更能提供你最新、最酷的焦點餐廳情報。

如果你是美食愛好者,那不能錯過這一款APP唷!

畫面截圖

PLAN
第一頁: 可用月曆模式瀏覽紀錄
PLAN_2
第二頁: 以列表方式顯式資料
PLAN_3
按進階搜尋後畫面,按搜尋之後會反回第二頁只是資料不同
PLAN_4
點選列表或日曆的單項說明後進入詳細資訊頁
PLAN_5
點第一、第二頁右上的新增後進入的新增畫面
PLAN_6
第三頁:以地圖去尋找附近的餐廳(未來可能會想改成可以找網友分享的紀錄)
PLAN_7
第四頁: 純粹顯示一個網頁,如果未連網則顯示沒有連網頁,這邊應該會用網頁去更新,提供好吃餐廳資訊
PLAN_8
第五頁: 介紹作者

因為是第一款APP,不知道這樣會不會觸犯到APP的上架條款

  1. 應用程式涉及了裸體、暴力、中傷他人
  2. 違反商標法
  3. 功能過於簡單或與iOS內建功能動覆
  4. 使用未開放的API
  5. 應用程式Bug、當機且無任何警告訊息
  6. 違反HIG(Human Interface Guide)使用者介面
  7. 軟體功能與描述不符,或是描述提到第三方的名詞,如:Android/BlackBerry
  8. 簽證檔需設定為Distribution(iOS Dev Center中設定)

希望有經驗的人可以給我一些建議,謝謝大家!

 

Posted on

IPhone元件介紹

表格視圖,文字視圖和Web視圖

  1. 狀態欄:狀態欄顯示與使用者裝置相關的重要資訊,包括電話信號強度,當前網路連接和電量資訊。
    螢幕快照 2013-08-26 上午1.14.48
  2. 導覽欄:導覽欄位於應用程式螢幕的上邊緣,在狀態欄之下。導覽欄通常會顯示當前視圖的標題,包含導覽控制元件,並在適當的情況下也可以包含作用於視圖內容的控制元件。
    螢幕快照 2013-08-26 上午1.15.53
  3. 工具欄:如果您的應用程式為使用者提供了一系列可在當前上下文中執行的動作,那麼您可以為使用者提供一個工具欄。工具欄的外觀要力爭與應用程式中的其它欄目的外觀保持一致。例如,如果您使用半透明的工具欄,就不要將它與不透明的導覽欄一起使用。此外,請避免在同一方向的不同螢幕中改變工具欄的顏色或半透明效果。
    螢幕快照 2013-08-26 上午1.17.07
  4. 標籤欄:標籤欄使使用能夠在應用程式的不同模式或不同視圖之間進行轉換,並且使用者應該能夠從應用程式的任何地方進入這些模式。
    螢幕快照 2013-08-26 上午1.19.50
  5. 在應用程式螢幕中使用視圖和控制元件
    螢幕快照 2013-08-26 上午1.21.38
  6. 表格視圖:裡面包含許多元素,並可以使用表格視圖實現常用的使用者操作。如:選項列表;導覽層次資訊;查看按概念分組的資訊
    螢幕快照 2013-08-26 上午1.23.11
    有幾個表格操作元素:
    1. 展開指示符:當這個元素出現時,使用者知道他們可以點選這一行中的任意位置來查看下一級的資訊,或是與當前列表項相關的選項。
      當選中一行後會顯示另一個列表時,應使用展開指示符。不要使用展開指示符來顯示某個列表項的詳細資訊;這種情況下應使用詳細資訊展開按鈕。
    2. 詳細資訊展開按鈕:使用者可以通過點選該元素來查看某個列表項的詳細資訊。在表格視圖中,可以在某一行中使用詳細資訊展開按鈕,來顯示與該列表項相關的詳細資訊。請注意,與展開指示符不同,詳細資訊展開按鈕所執行的動作可以獨立於行的選擇動作。
    3. 刪除按鈕:使用者通過點選該元素可以刪除對應的列表項。
    4. 刪除控制按鈕:使用者通過點選該元素可以顯示或隱藏每個列表項的“刪除”按鈕。
    5. 行插入按鈕:使用者通過點選該元素可以向列表中新增一行。
    6. 行排序控制元件:當該元素出現時,使用者可以拖動一行到列表中的另一位置上。
    7. 選定符號:該元素出現在列表項的右側,表明該項當前被選中。
    8. 開關控制元件
  7. 文字視圖
    螢幕快照 2013-08-26 上午1.29.31
  8. Web視圖:Web視圖是應用程式螢幕上可以顯示豐富的HTML內容的一片區域。除了顯示web內容之外,web視圖還提供了一些元素來支援使用者瀏覽開放的網頁。雖然您可以選擇向使用提供網頁瀏覽功能,但是最好避免讓您建立的應用程式看起來像是一個小型web瀏覽器。

應用程式控制元件

  1. 活動指示器:活動指示器表示有一項持續時間未知的任務或過程正在進行當中。(請參考UIActivityIndicatorView類)
    螢幕快照 2013-08-26 上午1.32.36
  2. 日期時間選擇器:日期時間選擇器為使用提供了一種簡單的選擇特定日期或時間的方式。(請參考UIDatePicker類)
    螢幕快照 2013-08-26 上午1.33.46
  3. 詳細資訊展開按鈕:詳細資訊展開按鈕提供了關於某一項的額外資訊或更詳細的資訊。(參考UIButton類)
    螢幕快照 2013-08-26 上午1.37.11
  4. 資訊按鈕(參考UIButton類)
    螢幕快照 2013-08-26 上午1.38.44
  5. 標籤:標籤是一種大小可變的靜態文字。(參考UILabel類)
    螢幕快照 2013-08-26 上午1.39.59
  6. 頁面指示符號(參考UIPageControl類)
    螢幕快照 2013-08-26 上午1.40.41
  7. 選擇器:選擇器是日期時間選擇器的通用版本,如果您需要顯示特別多數量的值,則您應該用表格視圖將這些值列出,而不是用選擇器。
    這是因為表格視圖俱有更高的高度,能夠更快速地進行滾動。(參考UIPickerView類)

    螢幕快照 2013-08-26 上午1.42.15

  8. 進度視圖:進度視圖顯示俱有確定持續時間的任務或過程的進度。如果您需要顯示未知持續時間的任
    務的進度,請使用活動指示器。(參考UIProgressView類)
    螢幕快照 2013-08-26 上午1.44.07
  9. 圓角矩形按鈕:圓角矩形按鈕是一種多功能的按鈕,您可以在視圖中使用它來執行一個動作。(參考UIButton類)
  10. 搜尋欄:搜尋欄是一片接收使用者輸入的文字的區域,您的應用程式可以以搜尋欄中的文字為輸入進行搜尋。(參考UISearchBar類)
    螢幕快照 2013-08-26 上午1.45.31
  11. 分段控制元件:分段控制元件是處在一條直線上的各段的集合,其中的每一段都相當於一個按鈕,可以顯示不同的視圖。(參考UISegmentedControl類)
    螢幕快照 2013-08-26 上午1.47.03
  12. 滑塊:滑塊允許使用者在允許的值的範圍內對一個值或一個過程進行調整。當使用者拖拉滑塊時,相應的值或過程也不斷被更新。(參考UISlider類)
    螢幕快照 2013-08-26 上午1.48.04
  13. 文字框:文字框是一個接收使用者輸入的圓角矩形的區域。當使用者點選文字框時,會出現一個鍵盤;當使用者點選鍵盤上的Return鍵時,文字框會以應用程式指定的方式處理使用者的輸入資訊。(參考UITextField類)
    螢幕快照 2013-08-26 上午1.49.26

用於工具欄和導覽欄的標準按鈕

有關這些按鈕的符號名稱和可用性的資訊請參考UIBarButtonSystemItem的文件。

Posted on

IPhone人機界面指南(HIG)

中文版下載:iPhone-HIG(人機界面指南)
英文版下載:MobileHIG

HIG(人機界面指南)是apple在審核app時,給開發者去參考的條文,
只要想要在ios平台上開發app的開發者,在上架時,都必需同意自己的應用程式符合裡面的規範。
這也是app開發者在開發完APP後,常常會被apple退件的原因。
因此建議有志要開發iphone app的人可以在開發前先研讀一下這本規範手冊。

HIG將應用程式分類成三大類型

  1. 生產力應用程式:幫忙使用者管理資訊及完成工作。資訊是階層架構的,可以透過向下深入更多層級而瀏覽更多資訊。
    效率型應用程式一般採用多個視圖,通常每一視圖顯示一個層次的資訊。使用介面通常簡單,整潔且由標準視圖和控制元件組成。效率型應用程式通常沒有很多自定義介面。原因在於效率型應用程式專注於資訊和任務,而非環境或體驗。

    螢幕快照 2013-08-25 下午2.18.11

  2. 融入式應用程式:遊戲是典型的例子,這種應用程式使用非常客製化的介面,讓使用者與裝置互動,HIG對這種應用程式的規範比較不那麼嚴格。
    使用者的焦點在於視覺上的內容和體驗,而非體驗背後的數據。
  3. 工具應用程式:透過盡可能少的互動與組態設定提供使用者特定的資訊。這種APP通常比生產力應用程式有更多的介面且更加被要求要符合HIG規範。
    實用型應用程式俱有視覺上的吸引力,但是又能夠突出它們顯示的資訊。使用者使用實用型應用程式來查看一些事情的狀態或是查找一些東西,所以使用者希望能夠快速方便地看到他們感興趣的資訊。因此,實用型應用程式的使用者介面十分簡潔,並提供簡單的,標準的視圖和控制元件。
    實用型應用程式一般以平面列表的方式顯示資訊;使用者通常不需要深入資訊層次結構。實用型應用程式中的每一個視圖通常都提供同樣的數據組織結構和細節深度,但是數據來源可以不同。由此,使用者可以打開單個實用型應用程式以對多個主題進行相似處理。一些實用型應用程式能顯示打開的視圖的數量;由此使用者可以按順序瀏覽,在一個視圖後選擇另一個視圖。(例如下圖使用者可以用上一層的設定界面讓下一層的天氣資料顯示不同地區的資訊)
    螢幕快照 2013-08-25 下午2.31.31

開發APP時要注意的點

  1. 畫面比電腦小很多:應考量將複雜的畫面簡化
  2. 記憶體有限:因為ios的記憶體不包括磁碟交換空間,所以在app開發時要注意不能同時使用太高的記憶體,ios在記憶體不足時,也是會從耗記憶體最高的app開始關閉。
  3. 每次只能顯示一個視窗:不能同時瀏覽多個視窗,只可以依序瀏覽。
  4. 同一時間只能運行一個程式:因為當接電話、切到另一個應用程式等,現有的應用程式將被關閉。應要注意在使用者離開再回來時的畫面順暢度。
  5. 簡單易懂的操作說明

 人機介面準則

  1.  當使用者操作螢幕上的對象時,這些對象保持可見,並且操作結果是一目了然的。
  2. 讓使用者做出選擇,而不是要求他們進行開放式的輸入,這樣能夠讓使用者能夠集中精力通過您的程式完成任務,而不是疲於記憶如何操作。
  3. 如果有一個動作,使用者需要長時間的等待,應該回饋一個音效或圖案。在完成資料下載時,也應讓使用者知道動作已完成。應隨時回饋使用者,讓他能了解現在系統正在做什麼。
  4. 使用者控制:由使用者來觸發和控制操作
  5. 美學完整性:美學上的完整性並不是用來衡量您的程式有多漂亮,而是衡量您程式的外觀和程式功能結合得有多好。例如,一個效率型應用程式應盡量精簡裝飾性的元素並置於背景中,同時使用標準控制元件和行為以突出任務。

優秀iPhone應用程式的共性

  1. 簡單和易用性
  2. 顯而易見:您可以最大限度地減少使用者必須選擇的控制元件數量,並清晰地標識這些控制元件以便使用者明白它們的功能。
  3. 由上而下的佈局:使用者可以用手指或大拇指點選一個iPhone OS的裝置的螢幕。使用者傾向於用他們的非慣用手握住裝置(或擱在手心),用慣用手的手指點選裝置。當使用大拇指時,人們要麼將裝置一手握住,然後用大拇指點選,要麼將裝置握於雙手間,同時用兩根大拇指點選。
    無論用哪一種方式,對於使用者來說螢幕最上方都是最顯眼的。因此把最重要的資訊放在上方。
  4. 最大限度地減少所需的輸入:向使用者請求輸入時,考慮使用表格視圖(或選擇器)來代替文字輸入框。
  5. 簡潔地表達資訊:當使用者介面上的文字簡短而又直接的話,使用者就能夠快速地理解它。
  6. 為控制元件提供指尖大小的目標區域:一個簡單易用的使用者介面應該合理地分配控制元件和其他使用者介面元素,使得使用者能夠更方便地點中控制元件。
  7. 將重點放在主要功能上:確定在每個上下文中甚麼是最重要。
  8. 有效地溝通:避免使用技術性術語,讓使用者看不懂
  9. 適當的支援手勢:例如在表格list裡抓取使用者向右滑動的手勢,來顯示刪除功能。
    螢幕快照 2013-08-25 下午11.10.05
  10. 慎重地融合品牌元素:應該以一種低調的方式引入您的品牌的顏色或圖片
Posted on

UIViewController切換、Notification、Delegate及繪圖練習

練習目標:http://www.youtube.com/watch?v=xiHBN2B1Vt4&list=UUPRP4bs_BNpx6XWI5Wm7O5g
老師範例:DrawSomething
我的作品:homework0810

在這個範例中,我使用了兩個viewController,去控制兩個頁面的畫面。

第一個用來顯示動畫的地方的viewController如下,
在這個頁面上除了球之外,還會有一個控制開關的鈕,當打開時代表不去管路徑如何直接讓球到圓點,
而關閉則會讓球一步步照著所繪的路線移動。
#import “ViewController.h”

@interface ViewController ()
@property (weak, nonatomic) IBOutlet UISwitch *straightSwitch;
@property (weak, nonatomic) IBOutlet UIImageView *myBall;

@end

@implementation ViewController

– (void)viewDidLoad
{
[super viewDidLoad];
}

– (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

/* 當切換畫面時會呼叫這個函數,可在此設定接收繪圖路徑資料的接收者為自己*/
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@”toDrawing”]){
ViewController2 * modalView = (ViewController2 *) segue.destinationViewController;
modalView.delegate = self;
}
}

/*這個函數是讓另一個ViewController去呼叫的,傳入path*/
-(void) DrawViewController:(ViewController2 *) ViewController path:(NSMutableArray *) path{
if([path count] == 0) return;
if(self.straightSwitch.on){
NSValue *value = [path objectAtIndex:[path count]-1];
[self performSelector:@selector(changePosition:) withObject:value afterDelay:0];
}else{
for (int i=0; i<[path count]; i++) { NSValue *value = [path objectAtIndex:i]; [self performSelector:@selector(changePosition:) withObject:value afterDelay:i*0.1]; } } } /*改變球的位置*/ -(void)changePosition:(NSValue *) value{ CGPoint point = [value CGPointValue]; CGRect rect = [self.myBall frame]; rect.origin.x = point.x - 25; rect.origin.y = point.y - 25; [self.myBall setFrame:rect]; } @end[/code] 第二個view則是用來讓使用者繪製路線的地方, 在這邊我又將繪圖的地方獨立出來成為另一個繪圖版 而我讓view與繪圖間用notification來傳遞資料。 [code lang="c"]#import "ViewController2.h" #import "DrawPanel.h" @interface ViewController2 () @property (weak, nonatomic) IBOutlet UIImageView *myBall; @property (weak, nonatomic) IBOutlet DrawPanel * myDrawPanel; @end @implementation ViewController2{ NSMutableArray * path; } @synthesize myDrawPanel; - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; //註冊監聽事件 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getPathData:) name:@"getPathData" object:nil]; } //監聽繪圖版的繪圖事件接收器 -(void) getPathData:(NSNotification *) note{ NSDictionary *dict = [note userInfo]; path = [dict valueForKey:@"path"]; } //將繪圖資料array傳回第一個view - (IBAction)ActionBtnTouch:(id)sender { [self dismissViewControllerAnimated:YES completion:^{ if([self.delegate respondsToSelector:@selector(DrawViewController:path:)]){ [self.delegate DrawViewController:self path:path]; } }]; } //在移除這個view時要順便將監聽事件移除 -(void)removeFromParentViewController{ [myDrawPanel removeObserver:self forKeyPath:@"price"]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end[/code] 這個則是繪圖版的code [code lang="c"]#import "DrawPanel.h" @implementation DrawPanel @synthesize path; - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code } return self; } //每次碰觸時都清空array的值 -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ path =[NSMutableArray array]; } //移動時將所移動到的位置紀錄下來 -(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ UITouch *touch = [touches anyObject]; CGPoint touchLocation = [touch locationInView:self]; NSValue *value = [NSValue valueWithCGPoint:touchLocation]; [path addObject:value]; [self setNeedsDisplay];//請view更新畫面 } //將資料傳給繪圖的view(發送notification) -(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ [[NSNotificationCenter defaultCenter] postNotificationName:@"getPathData" object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys: path , @"path",nil]]; } //實作繪圖方法 - (void)drawRect:(CGRect)rect{ CGContextRef context = UIGraphicsGetCurrentContext(); CGContextBeginPath(context); CGContextMoveToPoint(context, 55, 325); for(int i = 0;i<[path count];i++){ NSValue *value = [path objectAtIndex:i]; CGPoint newPoint = [value CGPointValue]; CGContextAddLineToPoint(context, newPoint.x, newPoint.y); } //CGContextClosePath(context); [[UIColor blackColor] setStroke]; CGContextDrawPath(context, kCGPathStroke); } @end[/code] 檔案下載:homework0810

Posted on 2 Comments

提升Flash效能的幾個注意事項

本文為看這篇文章之後的重點整理: http://www.adobe.com/devnet/flash/articles/efficiency-tips.html

這篇文章同時會對Flash開發程式師、動畫及美術有幫助。

提升效能

  1. 關掉沒有用到的物件的互動功能:
    減少畫面中active的物件能有效的增加效能,下面這段CODE可以關閉掉MovieClip和其他物件的所有互動關係。

    mouseEnabled = false;
    mouseChildren = false;

    如果仍要與其他的元件做互動則使用mouseChildren = false;就好。
    fig01

  2. 判斷何時該用點陣圖(Bitmap)何時該用形狀(Shape):
    使用Bitmap:當今天圖片有許多漸層、複雜圖案、顏色很多時,用Bitmap較好。
    使用Shape:當今天的圖片是大塊色塊組成,感覺不利於縮放、但卻不需要縮放時,則使用Shape。(點陣圖按CTRL+B)fig02
  3. 將線段轉成色塊:線段所需要耗用的效能較多,因此可將不必要的線段轉換為填色。(在修改>形狀>將線段轉換成填色)
    fig03
  4. 避免使用虛線或點線,因為此種線段很耗費效能。
  5. 避免使用圓角,圓角比起直角的處理上耗效能。
  6. 避免圖像的坐標坐落位置帶有小數點,因為小數點的運算比起整數會耗費更多效能。

向量圖形最佳化

  1. 減少圖形中的節點(按修改>圖像>最佳化),或使用平滑工具來減少節點。
    fig06
  2. 避免使用漸層:漸層是非常耗效能的,若是必需要使用漸層,可在完成後,將其轉為Bitmap格式,或者以純色去取代。
  3. 不要使用不必要大小的bitmap圖片,或多餘的aplha值=0的透明色塊。因為這樣會增加所耗費的效能。
    fig08
  4. 避免使用遮罩:使用遮罩時,每次進入下一個影格都會重新繪製並產生裡面的元件,因此會耗費大量的效能。所以我們要盡量避免使用遮罩功能,若真的有需要,可以改用程式裡的scrollRect功能來取代遮罩,這樣在實務上會省下許多的資源,缺點是只能做方形遮罩。
    fig09
  5. 靜態物件避免使用濾鏡特效:因為濾鏡特效是每次重繪時都要重新計算一次特效顯示的值,若是今天是靜態物件要做濾鏡效果,可先在PHOTOSHOP內做好,便可節省濾鏡特效的運算時間。
    fig10
  6. 將陰影效果的品質調低
    fig12
  7. 假如圖片要resize,不要直接在flash裡改變圖片大小,而應該在外部先做好resize再loading進來。可以使用mipmapping 來計算圖片的最佳大小。
  8. 降低影格速度
    fig13
  9. 避免MovieClip呈現槽狀結構
    fig14
  10. 動畫目標物件可設置快取為點陣圖以節省效能,但此時要注意不要對該元件設置任何濾鏡,不然他每一個影格都會重新快取一次目標物件,這會耗費非常高的效能。
    2013-08-23_165042
    fig15
  11. 濾鏡特效的設定值設為2的指數(ex: 2 4 8 16….)
    fig16
  12. 如果只是非常簡單的動畫(如單純的位移事件),可以用程式碼去替代(如TweenLite),會更高彈性且節省效能。
    fig18
  13. 2.5D的CPU圖像模式的運算會很耗效能,此時可以用”快取為點陣圖”來製作2.5D的圖像。
  14. 觀察重繪區塊,去知道是那邊耗費較多的效能。(在FLASH DEBUG按右鍵=>顯示重繪區域),盡量讓重繪的區塊降到最低。
  15. 假如要設一個元件為不可視,記得停掉該元件,避免他在背後繼續執行(下stop)。
    fig22
  16. 避免元件出現在可視範圍外,若是需要將元件移除或移入視線,可以使用addChild()及removeChild()
  17. 除非有需要做特殊的文字效果,否則不要使用TLF text Field。

最佳化檔案素材大小

  1. 選擇適當的圖像壓縮比、非必要時,避免勾選”平滑化”的選項。
    2013-08-23_180249
  2. 考慮使用一整張的圖片或是用BITMAP填色:因為若顯示的區塊很小,但我們用一張超大張的圖片來做填色,這一大塊的圖片都會被包在SWF裡,因此若遇到這個狀況,可以用CTRL+B將點陣圖打散成形狀,將其縮小後,再將其轉回點陣圖,直接使用該圖片做為底色。
    但假如現況是某一張小圖片,會重複的出現在眾多區塊做為底色,那這時後就推薦用以BITMAP來填滿區塊的方式。
  3. 盡量避免在畫面中RESIZE圖片的大小
    fig27
  4. 假使是很單純、色塊及線條極少的狀況,可以將點陣圖轉為向量圖,這樣可以縮小SWF的體積。(修改>點陣圖>轉換為向量圖)
    2013-08-23_190251
  5. 一格格的動畫改為移動漸變動畫(這樣是省空間,但會耗效能,所以要取決於現在你是需要效能或者空間,去決定該用那種作法)。
    fig29
  6. 當個向量圖會在很多地方重覆出現時,將其轉換為SYMBOLS(圖像),這樣會可以在很多地方共用一個資源。
  7. 要嵌入字型時,只嵌入自己所需要的字碼範圍
    fig30
  8. 可在發佈設定的地方建立專案的描述檔去觀察所有元件的大小(右顛齒輪可新增描述檔)
    2013-08-23_190943
  9. 在發佈設定=>SWF=>進階的地方,把”包含XMP中繼資料”選項取消,可以減少一些檔案大小。
    2013-08-23_191205

優雅的開發FLASH(重點整理)

  1. 避免元件的所在位置帶有小數點
    fig32
  2. 考慮現在該用BITMAP或是SHAPE
  3. 文字字型選擇”消除鋸齒增加易讀性”
  4. 除非必要,不要讓文字變成可選擇的
    fig35
  5. 沒有需要移動漸變的影格不要設為移動漸變,可按右鍵的”移除補間動畫”來移除。
    fig36
    2013-08-23_191910
  6. 將元件的APLHA設為0時,他依然會接受滑鼠事件,會耗費處理滑鼠事件的效能。將元件的VISIBLE設為FALSE,元件仍然在畫面上。最好的方式是將其整個圖像完全刪除。
  7. 不要把元件壓在另一個元件之下,因為他還是會耗廢效能,把他的visible設為false或直接移除都可節省效能。
Posted on 1 Comment

AS3的事件傳遞機制(Event、dispatchEvent及addEventListener)

當事件被發送出來之後。Event一般來説都會有一個Flow。 Flow分三個部分:

  1. Capture(捕獲階段)
  2. Targeting(目標階段)
  3. Bubbling(冒泡階段)

其關係圖如下:(來源為http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html)

efAzj

值得一提的是,在FLASH裡,只有和UI相關的物件,會有上圖所示的目標及補獲階段的事件流

一般像是Timer、Loader事件,是直接進入目標階段,並不會有上圖所示的事件流的流程。
在宣告Event時,也可以先指定事件是否要完整的跑完全部的流程。

宣告事件的語法及及參數意義如下:

var event:Event = new Event(type, bubbles, cancelable);

參數說明(官方文件)

  1. type:String是要發送的事件的識別名稱EX:Event.COMPLETE
  2. bubbles:Boolean預設值是false,這個值是用來設定是否要有冒泡階段,如果傳false,代表他不會跑完全部的流程,只會從Capture到target階段就停止
  3. cancelable:Boolean預設值是false,這個值是用來設定是否這個事件可以被event.preventDefault();取消,如果設定為true的話,代表此事件是可以被取消的(稍候在dispatchEvent會更詳述這部份)。
    常用的可以取消的事件有(cancelable為true):   FocusEvent.MOUSE_FOCUS_CHANGEFocusEvent.KEY_FOCUS_CHANGETextEvent.TEXT_INPUT

而發送事件則是用這一段程式碼:

var result:Boolean = box.dispatchEvent(event);

在發送事件時,要注意,假使今天我們是在STAGE裡面有一個box物件,那當我們用box.dispatchEvent(event);
即使事件是用box發送的,事件還是會從stage > root > box這樣跑。(請見上圖)
假如在建立Event時,bubbles設為true,那在上圖跑的流程為stage > root > box > root > stage
而若bubbles設為false,跑的流程則是stage > root > box

當我們用root.dispatchEvent(event);
bubbles設為true,上圖跑的流程為stage > root > stage
bubbles設為false,流程為stage > root

由此可知,事件流只會到目標(Target),就會停止往下傳
假如今天box裡面有一個物件button,若是用box.dispatchEvent(event)
button物件是不會接到事件流的。(因為到目標階段便會開始bubbles或停止)
並且假如今天root裡同時有box和box2兩個物件,
假使我們用box.dispatchEvent(event),則box2也不會接到事件。

至於第三個在建立Event的參數cancelable,不論值是true或false,都不會影響事件流的流程。
那他的作用是做什麼的呢?

我們可以發現,dispatchEvent有回傳一個布林值,cancelable就是在影響這個布林值的傳回參數。
今天我們假如要創建一個物件,但是要讓使用者有權力去阻止這個事件發生後的後續發展時,可以這樣寫

if(this.dispathEvent(event)){
     trace("success!");
     //在這寫後續發展
}

假使使用者在監聽的途中呼叫 event.preventDefault();,並且EVENT的cancelable為true時,dispatchEvent回傳的值就會是false,便不會執行trace("success!");那塊區塊。但假使EVENT的cancelable為false時,不論你在事件執行中有沒有呼叫event.preventDefault(),dispatchEvent回傳的值就會是true

一般我們會在傳遞事件的途中,去將事件攔截下來。增加監聽事件的程式碼如下

box.addEventListener(MouseEvent.MOUSE_DOWN,parent1Event);

官網關於addEventListener的說明如下

addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void

傳入的參數意義如下:

  1. type(String):事件名稱
  2. listener(Function):要執行的Function
  3. useCapture(Boolean):這個和本篇要說的事件流有關,當今天傳入的值為true,則只能在補獲階段去抓取事件,在冒泡階段是聽不到的。如果將 useCapture 設置為 true,則偵聽器只在捕獲階段處理事件,而不在目標或冒泡階段處理事件。
    如果 useCapture 為 false,則偵聽器只在目標冒泡階段處理事件
    要在所有三個階段都偵聽事件,需註冊 addEventListener 兩次:一次將 useCapture 設置為 true,一次將 useCapture 設置為 false。當今天若useCapture為true時,並不會聽到target階段時的事件但若為false時,也可以聽到target階段時的事件

    假使今天我們用box.dispatchEvent(event);來發送事件,
    然後box的監聽事件的useCapture設為true。ex : box.addEventListener(MouseEvent.CLICK, rootClick, true);
    這樣rootClick事件並不會被呼叫到。

    若box的監聽事件的useCapture設為false 。ex : box.addEventListener(MouseEvent.CLICK, rootClick, false);
    這樣rootClick事件便被呼叫到。

  4. priority(int):若今天同時有很多個監聽器同時監聽同一事件,可用這個值來設定那一個監聽器應該被優先執行。數字愈高代表會愈快執行該事件偵聽程式。
  5. useWeakReference(Boolean):是否使用弱關連。若是用弱關連,當被監聽的物件所指到的變數被指向記憶體的其他地方,這個關係也會一併被取消(物件會被GC回收)。但如果傳進的值是false,當今天被監聽的物件的其他關連被指向別處時,該物件不會被GC回收,需要手動removeEventListener時,該物件才會被GC回收。

除了上述的幾個參數外,還有幾個函數可以中斷事件流的流程。
那就是stopPropagation()stopImmediatePropagation()

那這兩個函數有什麼差別呢?

下圖可以很清楚的解釋差異:
stopPropagation():會把同一階層的其他事件跑完才停止。
5MYe3

stopImmediatePropagation():立刻停止之後所有的事件流。
jleAX

假如今天box同時有兩個監聽函數如下

box.addEventListener(MouseEvent.MOUSE_DOWN,eventMouseDownHandler1,false,1);
box.addEventListener(MouseEvent.MOUSE_DOWN,eventMouseDownHandler2,false,2);

則由於priority值的關係,eventMouseDownHandler2會先被執行,eventMouseDownHandler1在之後才被呼叫
那若是在eventMouseDownHandler2裡面呼叫event.stopPropagation(),eventMouseDownHandler1還是會被執行,才將事件流中斷。
但是若在eventMouseDownHandler2裡面呼叫event.stopImmediatePropagation(),則eventMouseDownHandler1就不會被呼叫到

相關資源

  1. AS3.0的事件機制(詳細)
  2. AS3筆記-事件流(event flow)
  3. Event propagation
  4. Introduction to event handling in ActionScript 3.0