2015年2月19日 星期四

UIView、UIWindow、UIScreen初探

UIView定義螢幕上一個矩型區域,並且管理該區域的內容;在執行時期UIView物件負責呈現該區域所有內容,並且處理與該區域的所有互動;一個UIView也可包含多個UIView物件,形成階段式的UIView(UIView Hierarchy)。


簡單來說螢幕上看到的都是UIView。


UIWindow管理及協調在App中的所有UIView物件,並與UIScreen合作顯示在實際的螢幕上,除非外接螢幕App只會有一個UIWindow物件,UIWidnow兩個主要的功能,一是容器顯示其下所有UIView物件,二是將事件分派到其下的UIView物件;UIWidnow也繼承至UIView,擁有UIView的所有特性。


簡單來說一個App只會有一個UIWindow,負責管理所有的UIVew,及分派事件給它們。


UIScreen定義實際螢幕屬性,iOS裝置有主要螢幕,及零或多個附加螢幕;UIScreen可用來取得主要的螢幕物件,及其他附加螢幕物件。每個UIScreen會定義螢幕的長寬及其他螢幕屬性,像是亮度。


UIWindow有screen屬性(UIScreen物件),代表顯示內容的螢幕;而UIView有window屬性,代表所屬的UIWindow。參考下圖會更清楚UIWindow、UIView在App中的位置。


core_objects_2x.png


以下介紹如何取得UIWindow及UIScreen,及它們常用的方法。

取得UIWindow



var window = UIApplication.sharedApplication().keyWindow


var window = UIApplication.sharedApplication().delegate?.window?


若在UIViewController中可以用以下方法取得UIWindow。


var window = self.view.window


取得UIWindow後可用下面的方法測試。


println("\(window!.subviews.count)")


UIWindow與UIView關係



若用Single View Application的subviews.count輸出其值是1,我們將此UIView描述輸出。


println("UIWindow First Subview Description:\(window!.subviews[0].description!)")


UIWindow First Subview Description:<UIView: 0x7fb83ae34540; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fb83ae34810>>
<UIScreen: 0x7fb83ad1bf20; bounds = {{0, 0}, {375, 667}}; mode = <UIScreenMode: 0x7fb83ac12dd0; size = 750.000000 x 1334.000000>>


下圖示意Single View Application,最底層是UIWindow,接下來放置UIView,最後才放置其它視覺化物件。


UIWindow及UIView.png


新建UIWindow



App建立時會自動為我們建立UIWindow,我們也可以建立自己的UIWindow來取代原本的UIWindow,參考以下程式碼。


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
self.window!.backgroundColor = UIColor.redColor()
self.window!.makeKeyAndVisible()
return true
}

UIWindow的rootViewController屬性



UIWindow要替換其中所有的內容,建議方式是新建一個ViewController,然後指定到rootViewController屬性,而不是直接新增Subviews,若直接新增則需自行維持View與ViewController之間的關連。以下程式碼將原本ViewController置換。


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


var navController: UINavigationController = UINavigationController()
self.window!.rootViewController = navController
let myLabel: UILabel = UILabel(frame: CGRectMake(0,0, 100,100))
myLabel.text = "Hello World"
navController.view.addSubview(myLabel)
navController.setToolbarHidden(false, animated: true)
return true
}


下圖說明rootViewController與subviews之間的差異。


rootViewController.png


取得UIScreen


var screen = UIScreen.mainScreen()


取得螢幕大小



bounds取得包含statusbar的App大小,applicationFrame取得不包含statusbar的App大小;可用以下程式碼做實驗。


var r = UIScreen.mainScreen().applicationFrame


println(r)
       
var r1 = UIScreen.mainScreen().bounds
       
println(r1)


輸出結果如下ApplicationFrame輸出寬度是375、高度是647,Y軸位置是20;而Bounds輸出寬度是375、高度是667,Y軸位置是0。


(0.0,20.0,375.0,647.0)  -> ApplicationFrame
(0.0,0.0,375.0,667.0)    -> Bounds


需要注意的是在iOS8後輸出的寬度及高度並非不變的,而是根據螢幕旋轉而變動,我們將螢幕旋轉90度後,輸出的值如下。


(0.0,0.0,667.0,375.0) ->  ApplicationFrame
(0.0,0.0,667.0,375.0) ->  Bounds


所以我們可以這特性很簡單的判斷螢幕是直向還是橫向。


      if (r.width < r.height)
      {


           println("Portrait 直向")


      }
      else
      {
           println("Landscape 橫向")
      }


截取螢幕畫面


UIScreen提供螢幕截取方法snapshotViewAfterScreenUpdates,其中afterUpdates是否要將最近的變動更新後再截取,常用的值是false要立即截取目前畫面;傳回的是UIView,需要自行轉存成影像檔案,並存到相簿中,參考以下程式碼。


       //截取螢幕畫面
       var currentview = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)


      //建立UIImage
       UIGraphicsBeginImageContext(currentview.frame.size)
       currentview.layer.renderInContext(UIGraphicsGetCurrentContext())
       let image = UIGraphicsGetImageFromCurrentImageContext()
       UIGraphicsEndImageContext()


       //儲存到相簿
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)

沒有留言:

張貼留言