查看滴滴开源的
DoraemonKit
以及阿里开源的
youku-sdk-tool-woodpecker
时, 看到启动入口均采用
UIWindow
来实现, 效果如下图悬浮的绿色按钮.
拖动悬浮按钮可以随手指一动(图上为模拟器效果), 点击悬浮按钮可以切换页面.
对这种效果的实现方式, 我的第一直觉是采用在 VC 的
touch
事件中进行处理, 但是按钮还需要有点击效果, 这个就难以处理了. 带着疑问查看源码, 发现两个项目中的悬浮框均是采用
UIWindow
的方式来实现, 十分巧妙. 自己关于
Window
的使用方式做了一些尝试, 遂有此文.
1 新建的
UIWindow
如何显示?
开始时, 我首先把创建的
UIWindow
添加到
keyWindow
上, 调用
show
方法显示正常, 但是在使用
Xcode
中的渲染工具
debug view hierarchy
查看 App 的渲染层级时, 直接崩溃. 反复查找, 移除添加到
keyWindow
的代码, 显示和渲染均正常.
UIWindow
属于一个完整渲染层的容器控件, 不能再添加其他
UIWindow
实例. 一个
App
应用属于一个最底层容器, 可以同时承载不同的
UIWindow
. 多个
UIWindow
在
App
中的显示顺序, 按照
windowLevel
的大小, 从前到后进行显示.
App
中多个
UIWindow
的响应, 和
View
中多个
子View
的响应方式一样,
hitTest
方法返回哪个
UIView
, 哪个对象就响应.
iOS13
中使用多场景
SceneDelegate
时, 在
- (void)scene: willConnectToSession: options:
方法中创建
Window
时, 要采用
initWithWindowScene:
的方式, 否则
Window
将无法显示. 可以采用修改
Xcode
模板的方式, 在新建工程时默认去除
SceneDelegate
, 操作方式可参考:
MyTemplate
.
2 keyWindow 和 delegate 的 Window
一般在
application:didFinishLaunchingWithOptions:
方法中新建
Window
, 作为
AppDelegate
的属性. 然后调用
makeKeyAndVisible
方法显示在 App 中. 此时通过
[UIApplication sharedApplication].keyWindow
获取的为此
Window
. 新建
UIWindow
实例, 调用
makeKeyAndVisible
后, 可以改变项目中的
keyWindow
. 比如系统的
UIAlertView
/
UIAlertController
/
KeyBoard
显示时, 会自动切换
UIApplication
的
keyWindow
. 将某些控件显示在
keyWindow
时, 需要注意变化, 避免控件意料之外的消失.
将
UIWindow
的
hidden
属性设置为
false
时, 即可在 APP 显示, 为何还要置位
KeyWindow
呢? 看一下文档上
makeKeyAndVisible
的介绍:
Use this method to make the window key without changing its visibility.
The key window receives keyboard and other non-touch related events.
This method causes the previous key window to resign the key status.
主要在第二点, 设置为
key window
后, 可以响应键盘和非触摸事件. App 中的事件一共包含 7 种:
typedef NS_ENUM(NSInteger, UIEventType) {
UIEventTypeTouches,
UIEventTypeMotion,
UIEventTypeRemoteControl,
UIEventTypePresses API_AVAILABLE(ios(9.0)),
UIEventTypeScroll API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 10,
UIEventTypeHover API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 11,
UIEventTypeTransform API_AVAILABLE(ios(13.4), tvos(13.4)) API_UNAVAILABLE(watchos) = 14,
后面 3 类为
iOS13
新增, 文档上也没有介绍. 日常开发中接触到的主要是前面 4 类. 第一类为
touch
事件, 是否为
keyWindow
均可以响应; 第二类为加速计事件, 主要指
shake
; 第三类为远程控制, 比如
airpod
对音量的控制; 第四类为按压事件, 文档上解释为
a physical button
,
iPhone
上符合条件的只有音量键和电源键.
UIWindow
在显示/隐藏、keyWindow 切换时,会发送以下 4 种通知:
UIWindowDidBecomeVisibleNotification
UIWindowDidBecomeHiddenNotification
UIWindowDidBecomeKeyNotification
UIWindowDidResignKeyNotification
监听以上通知,测试键盘和2/3/4 类事件发生时,多
UIWindow
的响应情况。粘贴代码过于繁琐,直接说下测试结论:
-
motion
中的
shake
事件中,
keyWindow
才可以响应;
-
监听音量键通知
AVSystemController_SystemVolumeDidChangeNotification
前,需要调用
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]
方法,非
keyWindow
也可以正常监听, 这和文档介绍的不同;
-
非
keyWindow
中的
UITextField
和
UITextView
控件响应时, 会自动将所在的
Window
置位
keyWindow
.
makeKeyWindow
和
makeKeyAndVisible
不同,看下
makeKeyWindow
的介绍:
This is a convenience method to show the current window and position it in front of all other windows at the same level or lower.
If you only want to show the window, change its hidden property to NO.
makeKeyWindow
会把当前
Window
放在其他同级别/低级别的
Window
前面,但是对事件响应没有丝毫影响。当
keyWindow
被隐藏后,系统会自动把级别最高的
Window
置为
keyWindow
.
3 多
Window
的使用
开头的例子,将悬浮框看做
UIWindow
的实例,可以响应 触摸事件。添加
panGesture
手势,在响应方法中,更新
UIWindow
的位置。同时在
UIWindow
添加
UIButton
控件,进而在点击方法中进行页面操作。自己简单实现了一下
FloatingView
使用
UIWindow
可以仿照系统
UIAlertView
的实现方式,随心所欲的
show
到最顶层。将
VC
上添加一个
UIWindow
属性(创建时默认为屏幕大小),自身作为
UIWindow
的根控制器,设置
UIWindow
的
hidden
进行显示。这种方式有一个注意点,
UIWindow
对
rootViewController
是强引用,会和
VC
造成循环引用,在
Window
隐藏时,主动将
rootViewController
置为 nil, 打破循环。
使用
Window
还可以实现顺利启动广告图。
可以将 App 看做一个容器,里面可以装多个
UIWindow
,显示顺序和
windowLevel
的大小有关。
多
UIWindow
间彼此独立,不能相互添加,否者渲染层级会出问题。非
keyWindow
和
keyWindow
间对系统事件的响应方式不同,通过
makeKeyWindow
可以调整多
Window
的显示,通过
makeKeyAndVisible
可以切换
keyWindow
. 通过多
UIWindow
,可以很方便的模仿
UIAlertView
的实现,实现自定义弹窗以及启动图。
Demo
查看滴滴开源的DoraemonKit以及阿里开源的youku-sdk-tool-woodpecker时, 看到启动入口均采用UIWindow来实现, 效果如下图悬浮的绿色按钮.悬浮按钮拖动悬浮按钮可以随手指一动(图上为模拟器效果), 点击悬浮按钮可以切换页面.对这种效果的实现方式, 我的第一直觉是采用在 VC 的touch事件中进行处理, 但是按钮还需要有点击效果, 这个就难以处理了. 带着疑问查看源码, 发现两个项目中的悬浮框均是采用UIWindow的方式来实现, 十分巧妙....
当我们点击区域3时,响应蓝色按钮的点击事件,点击区域1和2时,响应红色按钮的点击事件,对于区域1和3没什么好说的,那如何让红色按钮响应区域2的点击呢?这就是笔者今天要讲的内容。
大家应该都知道,事...
`[Orientation] BUG IN CLIENT OF UIKIT: Setting UIDevice.orientation is not supported. Please use
UIWindow
Scene.requestGeometryUpdate(_:)`
那我们看看 `
UIWindow
Scene.requestGeometryUpdate(_:)` 怎么使用呢?
目录实践参考系统响应阶段APP响应触摸事件触摸 事件 响应者触摸触摸事件响应者寻找最佳响应者(Hit-Testing)视图如何判断自己能否响应触摸事件?寻找最佳响应者的原理hitTest:withEvent:自定义hitTest:withEvent:触摸事件的响应响应链中的事件传递规则UIResponder、UIGestureRecognizer、UIControl的优先级UIGestureRecognizerUIControl
场景描述:
viewA上的80%被viewB遮挡
viewA上添加了pa
转载自:http://momirror.blog.163.com/blog/static/48418184201361541235481/
1、UIKeyboardWillShowNotification-将要弹出键盘
2、UIKeyboardDidShowNotification-显示键盘
3、UIKeyboardWillHideNotification-将要隐藏键盘
2.在data中创建标识isActive=0,这是为了初始化第一个标题为active
3.监听单击事件触发方法,传入点击元素的index改变标识
4.绑定class属性,判断当(index===标识)时添加active-class
<li v-for="(item,index) of list" :key="index"
:class="{active: isActive === index}"
上一节,我们主要讲了多进程时,主进程如何利用信号量控制子进程的,这一节我们主要讲解下子进程的启动,和执行的操作。
上一节讲过,真正创建worker子进程的函数是ngx_start_worker_processes,这个函数本身很简单:
src/os/unix/ngx_process_cycle.c
static void
ngx_start_worker_processes(ngx_cy...
在mater进程首先通过调用ngx_start_worker_processes(cycle, ccf->worker_processes,NGX_PROCESS_RESPAWN);函数启动worker进程。
其中ccf->worker_processes代表启动的worker进程个数
1.变量声明。
ngx_int_t i; ngx_channel_t ch;
2.打...
响应式布局可以让网站同时适配不同分辨率和不同的手机端,让客户有更好的体验。
为什么要使用响应式布局:
在之前使用固定宽高有一定的局限:屏幕越来越宽时,因为定 得宽是固定的,这样会让页面不美观屏幕越来越小时,因为定宽的局限会让用户看不到完整的页面内容
在这种情况下,响应式布局就出现了
响应式布局的实现(有五种方法)
1.百分比布局:百分比布局是相对于父元素进行布局,百分比能够对width,height,padding
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
其中,UIInterfaceOrientationPortrait表示竖屏方向,UIInterfaceOrientationLandscapeLeft表示横屏向左方向,UIInterfaceOrientationLandscapeRight表示横屏向右方向;
2. 在AppDelegate.m文件中添加以下代码:
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(
UIWindow
*)window {
if (self.allowRotation) {
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait;
其中,allowRotation是一个BOOL类型的变量,用于控制是否允许横竖屏切换;
3. 在需要进行横竖屏切换的场景中,添加以下代码:
- (BOOL)shouldAutorotate {
return YES;
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
其中,shouldAutorotate返回YES表示允许自动旋转,supportedInterfaceOrientations返回UIInterfaceOrientationMaskAll表示支持所有方向的旋转。
需要注意的是,以上的代码只是最基本的配置,如果需要更加详细的配置,可以参考苹果官方文档中的相关说明。同时,不同版本的
iOS
可能会有所不同,需要根据实际情况进行调整。