美文网首页
View编程指南3—Windows

View编程指南3—Windows

作者: 好_快 | 来源:发表于2017-11-21 17:03 被阅读7次

    系统化学习,知其然,知其所以然

    每个 iOS 应用程序至少需要一个窗口, 有些可能包含多个窗口。一个窗口对象有几个职责:

    1. 作为一个容器包含应用程序的可见内容。
    2. 将触摸事件传递到视图和其他对象过程中起着关键作用。
    3. 和视图控制器协作,处理设备旋转方向。

    在iOS中,Windows没有标题栏,关闭框或其他视觉装饰。一个窗口始终只是一个或多个视图的空白容器。此外,应用程序不会通过显示新窗口来更改其内容。如果要更改显示的内容,请改为改变窗口的最前面的视图。

    大多数iOS应用程序在其生命周期中只创建并使用一个窗口。该窗口占据设备的整个主屏幕,并在应用程序生命周期的早期从应用程序的主要 storyboard/nib 文件(或以编程方式创建)加载。但是,如果应用程序支持使用外部显示器进行视频输出,则可以创建一个额外的窗口来在该外部显示器上显示内容。所有其他窗口通常由系统创建,通常是为了响应特定事件(例如来电)创建的。

    一、关于Windows的作用

    对于许多应用程序,APP 与窗口交互的唯一时间是在启动时创建窗口的时间。 但是,可以使用窗口对象来执行其他任务:

    1. 进行坐标转换。

    2. 使用窗口通知来跟踪与窗口相关的更改。 Windows会在显示或隐藏通知或者接受或退出密钥状态时生成通知。 您可以使用这些通知在应用程序的其他部分执行操作。 有关更多信息,请参阅监视窗口更改。

    二、Window的创建和设置

    创建Window有两种方式: 编程方式和Interface Builder。有以下注意点

    1. 在启动时创建窗口,保留该窗口并将其引用存储在AppDelegate中。
    2. 如果创建额外的窗口,让应用程序在需要时创建它们。 例如,支持在外部显示器上显示内容,则应在显示器连接成功后创建相应窗口。
    3. 无论应用程序是启动到前台还是后台,都应始终在启动时创建应用程序的主窗口。
    4. 如果应用程序直接进入后台,则应避免在应用程序进入前台之前显示窗口。

    2.1 创建方式一:Interface Builder

    分为2种使用场景:新工程和旧工程

    • 新建工程,Xcode的工程模板会自动完成创建工作,文件命为 Main.storyboard(不同时期名称不同)。
    • 从代码迁移到 Interface Builder ,除了拖拽一个 Window 之外,还需要完成以下操作
      • 想在运行时访问,一般做法是创建一个引用保存到 AppDelegate 对象中
      • 修改 Info.plist 文件中 UIMainStoryboardFile 的值,以便AppDelegate调用- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;时,Main.storyboard 已经加载完毕。

    2.2 创建方式二:代码创建

    //首先,在AppDelegate中创建一个引用,方便运行时访问
    @property (strong, nonatomic) UIWindow *window;
    
    //然后创建对象
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        //注意一定要全屏尺寸
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] ;
        return YES;
    }
    

    2.3 添加内容

    通过

    - (void)addSubview:(UIView *)view;
    

    添加view,为了方便更换显示内容,使用一个rootView,其他view添加到rootView,更换显示内容直接更换rootView即可,记得适配设备屏幕尺寸。

    此外,Window 提供了一个rootViewController属性

    @property(nonatomic, strong) UIViewController *rootViewController;
    

    UIKit 自动使用其view作为 window 的 rootView ,并且自动适配设备屏幕尺寸。

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] ;
        
        ViewController *rootViewController = [[ViewController alloc] init];
        
        self.window.rootViewController = rootViewController;
        
        return YES;
    }
    

    三、监测 Window 状态变化

    可以通过以下通知来追踪状态变化

    //显示
    UIWindowDidBecomeVisibleNotification
    //隐藏
    UIWindowDidBecomeHiddenNotification
    //关键窗口
    UIWindowDidBecomeKeyNotification
    //非关键窗口
    UIWindowDidResignKeyNotification
    
    • 显示或隐藏一个窗口,传递UIWindowDidBecomeVisibleNotification和UIWindowDidBecomeHiddenNotification通知。当应用程序进入后台执行状态时,不会传递这些通知。即使窗口在应用程序处于后台时并未显示在屏幕上,但在应用程序的上下文中,它仍被视为可见。

    • UIWindowDidBecomeKeyNotification和UIWindowDidResignKeyNotification通知帮助识别哪个窗口是关键窗口(当前正在接收键盘事件和其他非触摸相关的事件)。触摸事件传递到发生触摸的窗口,而没有相关坐标值的事件被传递到的关键窗口。KeyWindow 只有一个。

    四、在外部显示器上显示内容(Displaying Content on an External Display)

    要在外部显示器上显示内容,必须为应用程序创建一个额外的窗口,并将其与代表外部显示器的屏幕对象相关联。一旦窗口与正确的屏幕相关联,您可以添加视图并显示它,就像您为应用程序的主屏幕一样。

    UIScreen维护一个代表可用硬件显示的屏幕对象列表。通常,只有一个屏幕对象表示任何基于iOS的设备的主显示屏,但是支持连接到外部显示屏的设备可以具有可用的附加屏幕对象。支持外部显示的设备包括具有Retina显示屏和iPad的iPhone和iPod touch设备。较旧的设备(如iPhone 3GS)不支持外部显示器。

    注意:由于外部显示本质上是一个视频输出连接,因此没有触摸事件。另外,应用程序负责根据需要更新窗口的内容。因此,要镜像主窗口的内容,应用程序需要为外部显示器的窗口创建一个重复的视图,并保持同步更新。

    可以使用 AirServer 软件在 Mac 电脑上模拟一个支持 AirPlay 的屏幕

    基本过程如下

    1. 在应用程序启动时,注册屏幕连接和断开通知。

    2. 当在外部显示器上显示内容时,请创建并配置一个窗口。

      1. 通过UIScreen的 screens 属性获取外部显示的屏幕对象。
      2. 创建一个UIWindow对象,并根据屏幕(或内容)适当地调整它的大小
      3. 将外部显示的UIScreen对象分配给窗口的screen属性
      4. 根据需要调整屏幕对象的分辨率以支持您的内容
    3. 显示并正常更新窗口。

    4.1 处理屏幕连接和断开通知(Handling Screen Connection and Disconnection Notifications)

    @interface ViewController ()
    {
        UIWindow *_secondWindow;
    }
    @end
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        [self setupScreenConnectionNotificationHandlers];
    }
    
    //注册通知
    - (void)setupScreenConnectionNotificationHandlers
    {
        NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
        //已连接
        [center addObserver:self selector:@selector(handleScreenConnectNotification:)
                name:UIScreenDidConnectNotification object:nil];
        //已断开
        [center addObserver:self selector:@selector(handleScreenDisconnectNotification:)
                name:UIScreenDidDisconnectNotification object:nil];
    }
    
    - (void)handleScreenConnectNotification:(NSNotification*)aNotification
    {
        UIScreen*    newScreen = [aNotification object];
        CGRect        screenBounds = newScreen.bounds;
     
        if (!_secondWindow)
        {
            _secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
            _secondWindow.screen = newScreen;
     
            // 初始化 UI
            [viewController displaySelectionInSecondaryWindow:_secondWindow];
        }
    }
     
    - (void)handleScreenDisconnectNotification:(NSNotification*)aNotification
    {
        if (_secondWindow)
        {
            // 隐藏并移除window.
            _secondWindow.hidden = YES;
            _secondWindow = nil;
     
            // 同步主屏幕和外接屏幕显示内容
            [viewController displaySelectionOnMainScreen];
        }
     
    }
    
    

    4.2 配置外部显示器的窗口(Configuring a Window for an External Display)

    - (void)checkForExistingScreenAndInitializeIfPresent
    {
        if ([[UIScreen screens] count] > 1)
        {
            // 关联 window 和 second screen.
            // 主屏幕 index 0.
            UIScreen*    secondScreen = [[UIScreen screens] objectAtIndex:1];
            CGRect        screenBounds = secondScreen.bounds;
     
            _secondWindow = [[UIWindow alloc] initWithFrame:screenBounds];
            _secondWindow.screen = secondScreen;
     
            // 添加一个 rootView 到 window
            UIView*            whiteField = [[UIView alloc] initWithFrame:screenBounds];
            whiteField.backgroundColor = [UIColor whiteColor];
     
            [_secondWindow addSubview:whiteField];
    
            // 中间放一个 label.
            NSString*    noContentString = [NSString stringWithFormat:@"<no content>"];
            CGSize        stringSize = [noContentString sizeWithFont:[UIFont systemFontOfSize:18]];
     
            CGRect        labelSize = CGRectMake((screenBounds.size.width - stringSize.width) / 2.0,
                                        (screenBounds.size.height - stringSize.height) / 2.0,
                                        stringSize.width, stringSize.height);
     
            UILabel*    noContentLabel = [[UILabel alloc] initWithFrame:labelSize];
            noContentLabel.text = noContentString;
            noContentLabel.font = [UIFont systemFontOfSize:18];
            [whiteField addSubview:noContentLabel];
     
            // 显示 window.
            _secondWindow.hidden = NO;
        }
    }
    

    4.3 配置外部显示器的屏幕分辨率(Configuring the Screen Mode of an External Display)

    根据显示内容,可以在将窗口与屏幕关联之前更改屏幕分辨率。 许多屏幕支持多种分辨率,使用不同的长宽比。屏幕支持的分辨率可以从 UIScreen 的 availableModes 属性获取屏幕支持的分辨率列表

    @property(nonatomic, readonly, copy) NSArray<UIScreenMode *> *availableModes;
    

    选择合适的分辨率然后设置

    @property(nonatomic, strong) UIScreenMode *currentMode;
    

    有关屏幕分辨率的更多信息,请参阅UIScreenMode参考。

    参考文章

    相关文章

      网友评论

          本文标题:View编程指南3—Windows

          本文链接:https://www.haomeiwen.com/subject/zumwvxtx.html