弄清楚程式語言只是開發軟體的第一步,在程式語言之後,你需要習慣這個平台上已經有了那些既有的慣例,而既有的慣例除了寫作風格之外,還要適應原廠的 Bug 及其莫名其妙的設計。像 UIKit 這套 Framework 有些地方實在是設計得莫名其妙,而且是讓你在工作的時候忍不住想要罵人的那種。
比方說,不管你寫什麼程式,你都有極大的機率會用到問人家好不好,要不要繼續的對話框,像是 Javascript 的 alert()
或 .Net Framework 裡頭的 Message.Show()
,這種對話框幾乎都是在你呼叫了 function 之後,就把到底按了哪個按鈕回傳給你。UIKit 偏偏就不是這樣設計,在呼叫了 UIAlertView 之後,偏偏要用 delegate method (或是所謂的 callback)的方式回傳。
雖然在 Cocoa 或是 UIKit 裡頭到處都在用 delegate,但是用這種方式處理 UIAlertView 實在很討厭-最常見的問題就是,假如說我在同一個 controller class 裡頭用上了一堆的 UIAlertView,而每個 UIAlertView 在處理回傳的按鍵的時候,要做不同的工作,而 delegate method 卻又全都是同一個名字,那要怎樣判斷到底是那個 UIAlertView 回傳的結果?
直覺可以想到一種方法是,生出很多不同的物件,分別作為不同的 UIAlertView 的 delegate-人活的好好的幹什麼生出一堆只負責處理對話框 delegate method 的 class?另外一種方法則是,在 alertView:clickedButtonAtIndex:
等 method 中,因為會把 UIAlertView 物件傳進來,那麼,只要比對指標,就可以知道是那個對話框了-如此一來,當你每次建立 UIAlertView 物件,都必須要把這個物件找個地方放起來,如果你用到了十個對話框,就代表你需要建立十個 class 為 UIAlertView 的成員變數,如此一來,絕對可以有效的讓你的程式變得無比醜陋。
有天實在覺得很生氣,寫了一個負責管理 UIAlertView 的 Singleton controller class(以下稱為 ZBAlertViewController)。簡單講,就是所有需要呼叫 UIAlertView 的時候,都由這個 class 生出 UIAlertView,然後將 UIAlertView 的 delegate 只到它身上。而外部在呼叫 ZBAlertViewController 的時候,同時指定 ZBAlertViewController 的 delegate 與一個 selector,在 ZBAlertViewController 收到 alertView:clickedButtonAtIndex:
之後,再對自己的 delegate 來 perform 指定的 selector。收工。
雖然這種麻煩是有辦法解決,但是 UIKit 本身幹嘛製造這種麻煩?
而你想要在你的 iPhone/iPod Touch 程式裡頭播放一段影片,自然是會用上 MPMoviePlayerController。用這個工具播放影片是很方便,只要指定影片的 URL,生出物件,馬上就可以開始播放,然後你還可以指定播放器的背景顏色、影片內容的縮放模式,播放介面也可以選擇三種模式-沒有任何操作介面、完整介面或只能調整音量。另外,MPMoviePlayerController 也會在影片改變縮放模式、已經載入足夠資料可以播放以及播放完畢的時候發出通知。
然後你會突然發現一件事情-
在完整播放模式下,播放器上面有跳到前一部與下一部影片的按鈕。當使用者按到這兩個按鈕的時候,完全沒有送給你任何事件,而且在文件上面,沒有告訴你怎樣可以隱藏這兩顆按鈕。整個 MPMoviePlayerController 都被包裝起來,叫你不要深究裡頭,但是 MPMoviePlayerController 卻沒有幫你處理這兩顆按鈕。太棒了。
如果你試著監聽所有的 Notification,是可以在使用者按下這兩顆按鈕的時候,收到一些由不在文件上的 class(如 MPAVController)所發出的一些不在文件上的通知(如 MPAVControllerPlaybackStateChangedNotification),但是呢,在按下前一部或下一部按鈕的時候,會收到的通知,可說完全一模一樣,這條路行不通。
從網路上的討論來看,目前國外幾家軟體-如vSNAX,要實作這兩顆按鈕的功能,方法都是,在 MPMoviePlayerController 的播放畫面出現的時候,軟體會多出一個用來播放影片的 UIWindow 物件,於是你可以從這個 UIWindow 物件裡頭的 subview 裡頭的 subview 裡頭的 subview 裡頭的 subview…慢慢去找這兩顆按鈕到底在哪裡,找到之後,你就可以或是把這兩顆按鈕換掉,或是再做兩顆按鈕蓋在上面,或是要這兩顆按鈕去做別的事情-總之,你的程式裡頭就是要一段極其醜陋的部份,用來找到這個 UIWindow 物件裡頭的 subview 裡頭的 subview 裡頭的 subview 裡頭的你想要的 subview。
而你也知道,用這個方法解決問題的後遺症就是-假如哪天蘋果又做了一個版本的 OS 升級,把 MPMoviePlayerController 裡頭每個 view 的順序又改過一次,你的程式就爆炸了。而以現在蘋果的上架程序,你大概在改過之後的一兩週之後,才能將改好的版本送到用戶手上。
話說,你找到了播放畫面是那個 view 之後,也是可以做些有趣的事情。例如-
嗯。有加分。
你好,
我目前想寫個streaming audio player
mpmovieplayercontroller可以播放http的audio file
不過我又想客製ui介面,讓他不要變成fullscreen
不知道有什麼方法可以操作movieplayer的view?
謝謝
最簡單的方法就是,不要用 MPMoviePlayerController ,用 AVAudioPlayer 寫。
BTW,AVAudioPlayer 是 OS 2.2 限定。
問題是AVAudioPlayer只能播放local file
要播放http的streaming audio,似乎只能用AudioFileStream來寫
如果可以客製MoviePlayer的UI介面就好了 XD
請問要怎麼抓取MoviePlayer中的view阿?
這真的是暴很大,暴不用錢 XD
用async的方式,才能確保螢幕上該動的東西會繼續動,這是為什麼大多數的ui framework都用這樣的方式來處理,尤其是compile的成品越接近機械碼越是如此,因為沒有東西可以在中間緩衝。就連Flash actionscript 3這樣有好幾層(layer)緩衝空間的東西,也都是如此要求人撰寫了。
至於解決這種問題,多半都是用:「直覺可以想到一種方法是,生出很多不同的物件,分別作為不同的 UIAlertView 的 delegate」的方法。這個方法在design pattern的討論中可以進一步演化出一些作法,如mediator或是strategy。
當程式越寫越大,妳會發現這些delegate的組合關係多半就是那幾個形狀,於是就可以獨立成一個一個的class,用factory pattern解決生成的問題。
一點感想
問題就出在- Modal Window 出現的時候,螢幕上面其他的東西幹嘛還要動呢?
Hi, 我最近也碰到这个头痛的问题,我的解决方案是这样的:UIView 有一个可以作为唯一标识的 tag property, 我给不同的 UIAlertView 物件赋以不同的的 tag, 这样在 delegate method 里就可以知道是哪个物件呼叫了。
請問MPMoviePlayerController 或 AVAudioPlayer 可以做到live streaming嗎
查了一下資料目前只有wowza 跟 real player系列的media server 才支援
您好,我想請教一下。
您在文中提到" UIWindow 物件裡頭的 subview 裡頭的 subview 裡頭的 subview 裡頭的 subview",這個部分要怎麼找到前一個/後一個事件的按鈕呢。
請問可以提示我一下嗎?謝謝~
程式碼是寫給客戶的,所以程式碼就沒辦法提供了。
[UIApplication sharedApplicaiton] 有一個叫做 windows 的屬性,裡頭是目前這個應用程式用到了哪些 UiWindow 物件,當播放器開始播放的時候,就會多出一個 window,而UiWindow 是繼承自 UIView,UiView 上面疊了什麼,都在subViews 屬性裡頭,反正就是一路 enumerate NSArray 裡頭的東西,把這個 view 的 class 倒出來看看(用NSLog(@”class:%@”, NSStringFromClass([aView class]))..之類的),看看某個 view 的 class 是不是像是某種 button 那樣的東西。
需要注意的是,在 2.0 OS 上,當你產生這個 player 物件的時候,就會開始建立這個 UIWindow以及相關的 view 物件,但是,在 3.0 上面,則是會確定等到開始載入影片之後,才會開始產生播放器 UI,所以,當你要做上面說的找 subview 那件事情的時候,要有一定的 delay,但是影片會花多久才會載入,又是不確定的。
所以呢,你就可能需要監控一下 [UIApplication sharedApplicaiton].windows 裡頭的變化,可以試試看用 KVO 做這件事情。
謝謝你的回應~
"在 3.0 上面,則是會確定等到開始載入影片之後,才會開始產生播放器 UI。"
我的解決方式是去註冊 UIWindowDidBecomeKeyNotification,
等撥放器變成 key window 後在去找裡面的物件,如此就可以找到Previous/Next event 的按鈕了。
目前還遇到一個問題,我想取代掉原本Previous/Next event 的 delegate。
我的做法是先試著找出那個事件,然後移除預設的關閉視窗的事件。
我列舉出該按鈕 UIControl 所有註冊的事件,發現找不到任何註冊的事件。
因此無法移除原本預設的 delegate (關閉視窗),請問你之前有遇過這個問題嗎?
对于alertView,你可以用alertView.title来判断啊,判断某个警报的标题,然后选择不就行了?
我覺得比對 title 是個頂爛的主意。
title 不是一個 unique 的東西,同樣的alert 提示,應該要做相同的事情,但是很有可能會拿到不一樣的 title-title 可能因為在不同語系下會有不同的翻譯,例頭的訊息也可能跟著情境不同而有所改變,不太可能拿這種東西來做判斷。
关于视频播放这块,我也没有什么好办法,我之前那个公司,都是自己写一个播放器,不过是有点麻烦而已。
樓上提到了Title,而我想到和使用的方法則是幫AlertView設定tag
雖然多此一舉,但或許只要規格統一,還是可以有效率的辨識AlertView