在 iOS 9 中检测应用程序是否处于滑过或拆分视图模式 运行
Detect if app is running in Slide Over or Split View mode in iOS 9
在 iOS 9 中,是否可以在 iOS 9 的侧拉或拆分视图模式下检测应用何时处于 运行?
我已经尝试通读 Apple 的 documentation 关于 iOS 9 多任务处理,但没有任何运气…
我问是因为我的应用程序中可能有一个功能,当应用程序在侧拉中打开时我想禁用该功能。
横向尺寸 class 在滑过或 33% 拆分视图时会更紧凑。不过,我认为一旦达到 50% 或 66% 就无法检测到。
您可以同时查看 -willTransitionToTraitCollection:withTransitionCoordinator: 的大小 class 和 viewWillTransitionToSize:withTransitionCoordinator: 的 CGSize。不过不建议对大小值进行硬编码。
经过多次 'tinkering',我为我的应用程序找到了一个可能适合您的解决方案:
在 AppDelegate.swift 中,创建以下变量:
var slideOverActive: Bool = false
然后,在所有视图控制器中,将 UIApplicationDelegate 添加到 Class 定义中,创建一个 appDelegate 变量,然后添加以下 traitCollectionDidChange 函数:
class myViewController: UIViewController, UIApplicationDelegate {
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
let screenWidth = UIScreen.mainScreen().bounds.width
if previousTraitCollection != nil {
let horizontalSizeClass: Int = previousTraitCollection!.horizontalSizeClass.rawValue
if screenWidth == 1024 || screenWidth == 768 { // iPad
if horizontalSizeClass == 2 { // Slide Over is ACTIVE!
appDelegate.slideOverActive = true
} else {
appDelegate.slideOverActive = false
}
}
}
}
}
然后,在您的代码中任何您想检查滑盖是否处于活动状态的地方,只需检查:
if appDelegate.slideOverActive == true {
// DO THIS
} else {
// DO THIS
}
这有点变通,但目前对我有用。
旅途愉快!
只需检查您的 window 是否占据了整个屏幕:
BOOL isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
如果这是错误的,那么您 运行 处于拆分视图或幻灯片中。
这里是代码片段,无论轮换如何,它都会自动维护这个标志
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
我来晚了,但如果你想要一个独立于方向的 属性,试试这个:
extension UIApplication
{
func isRunningInFullScreen() -> Bool
{
if let w = self.keyWindow
{
let maxScreenSize = max(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let minScreenSize = min(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let maxAppSize = max(w.bounds.size.width, w.bounds.size.height)
let minAppSize = min(w.bounds.size.width, w.bounds.size.height)
return maxScreenSize == maxAppSize && minScreenSize == minAppSize
}
return true
}
}
而且我真的迟到了!但是,尽管如此,这里有一个简单、快速的解决问题的方法。使用 let width = UIScreen.mainScreen().applicationFrame.size.width
我们可以检测我的应用程序 window 的宽度,然后当它小于某个数字时(即在 iPhone 屏幕上或在拆分视图中)会发生一些事情,对让不同的事情发生在较小的屏幕上。为了让计算机一遍又一遍地检查宽度,我们可以 运行 每百分之一秒一个 NSTimer ,然后如果宽度是 higher/lower 就做一些事情。
一些给你的测量值(你必须决定让东西出现的宽度above/below):
iPhone 6S Plus: 414.0mm
iPhone 6S: 375.0mm
iPhone 5S: 320.0mm
iPad(纵向):768.0mm
iPad(1/3分割视图):320.0mm
iPad Air 2(1/2分割视图):507.0mm
iPad(横向):1024.0mm
这是一个代码片段:
class ViewController: UIViewController {
var widthtimer = NSTimer()
func checkwidth() {
var width = UIScreen.mainScreen().applicationFrame.size.width
if width < 507 { // The code inside this if statement will occur if the width is below 507.0mm (on portrait iPhones and in iPad 1/3 split view only). Use the measurements provided in the Stack Overflow answer above to determine at what width to have this occur.
// do the thing that happens in split view
textlabel.hidden = false
} else if width > 506 {
// undo the thing that happens in split view when return to full-screen
textlabel.hidden = true
}
}
override func viewDidAppear(animated: Bool) {
widthtimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "checkwidth", userInfo: nil, repeats: true)
// runs every hundredth of a second to call the checkwidth function, to check the width of the window.
}
override func viewDidDisappear(animated: Bool) {
widthtimer.invalidate()
}
}
希望对前来偷看的人有所帮助!
我最近必须确定基于应用程序的显示样式,不仅包括它是否更改为拆分视图或滑过,还包括应用程序使用了屏幕的哪一部分(完整,1/3 , 1/2, 2/3).将其添加到 ViewController 子类能够解决问题。
/// Dismisses this ViewController with animation from a modal state.
func dismissFormSheet () {
dismissViewControllerAnimated(true, completion: nil)
}
private func deviceOrientation () -> UIDeviceOrientation {
return UIDevice.currentDevice().orientation
}
private func getScreenSize () -> (description:String, size:CGRect) {
let size = UIScreen.mainScreen().bounds
let str = "SCREEN SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
private func getApplicationSize () -> (description:String, size:CGRect) {
let size = UIApplication.sharedApplication().windows[0].bounds
let str = "\n\nAPPLICATION SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
func respondToSizeChange (layoutStyle:LayoutStyle) {
// Respond accordingly to the change in size.
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
private func determineLayout () -> LayoutStyle {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
return .iPadFullscreen
}
// Set a range in case there is some mathematical inconsistency or other outside influence that results in the application width being less than exactly 1/3, 1/2 or 2/3.
let lowRange = screenWidth - 15
let highRange = screenWidth + 15
if lowRange / 2 <= appWidth && appWidth <= highRange / 2 {
return .iPadHalfScreen
} else if appWidth <= highRange / 3 {
return .iPadOneThirdScreen
} else {
return .iPadTwoThirdScreeen
}
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
respondToSizeChange(determineLayout())
}
添加到@Tamas 的回答:
这是无论轮换如何自动维护此标志的代码片段。
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
重新打包所有这些的另一种方式
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !window.frame.equalTo(window.screen.bounds)
}
}
那你就可以
UIApplication.shared.isSplitOrSlideOver
在 Swift
UIApplication.sharedApplication.isSplitOrSlideOver
在 Objective-C
请注意,在 Swift 中,window
对象是双可选的... !
对于 iOS 13+(注意,我还没有自己测试 iOS 13 代码)
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let window = self.windows.filter({ [=11=].isKeyWindow }).first else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
正在尝试 [UIScreen mainScreen].bounds,
self.window.screen.bounds,
self.window.frame,
UIApplication.sharedApplication.keyWindow.frame 等等,唯一可行的解决方案是弃用方法
CGRect frame = [UIScreen mainScreen].applicationFrame;
我是这样修复的
CGRect frame = [UIScreen mainScreen].applicationFrame;
frame = CGRectMake(0, 0, frame.size.width + frame.origin.x, frame.size.height + frame.origin.y);
self.window.frame = frame;
类似于 Dan Rosenstark 的解决方案,但更改为在新的 iPad Pro 上工作,它似乎报告了不同的框架和 screen.bounds 高度,具体取决于它是否直接 运行设备通过 Xcode,或者通过 TestFlight 或 App Store 编译发布。通过 AS 或 TF 时,高度将 return 980,而不是通过 Xcode 应该喜欢的 1024,导致它不可能 return true。
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
通过使用下面的代码,您可以检查 splitViewController 是否已折叠
if splitViewController?.isCollapsed ?? false {
// splitview collapsed
} else {
// splitview not collapsed
}
我对@Michael Voccola 解决方案进行了编辑,解决了方向问题
我在我的情况下使用这种方式来检测所有 iPad 分屏状态和处理布局
只需调用 determineLayout()
即可获取当前 layoutStyle
private func getScreenSize() -> CGRect {
let size = UIScreen.main.bounds
return size
}
private func getApplicationSize() -> CGRect {
let size = UIApplication.shared.windows[0].bounds
return size
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
func determineLayout() -> LayoutStyle {
if UIDevice.current.userInterfaceIdiom == .phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
// full screen
return .iPadFullscreen
}
let persent = CGFloat(appWidth / screenWidth) * 100.0
if persent <= 55.0 && persent >= 45.0 {
// The view persent between 45-55 that's mean it's half screen
return .iPadHalfScreen
} else if persent > 55.0 {
// more than 55% that's mean it's 2/3
return .iPadTwoThirdScreeen
} else {
// less than 45% it's 1/3
return .iPadOneThirdScreen
}
}
这是我在 iPhone/iPad iOS 应用程序中使用的没有常量的更简单且不那么脆弱的方法。
此代码还区分滑过和拆分视图。
为清楚起见,我在此处返回字符串值,您可以随意使用枚举值并根据您的应用合并全屏的两种情况。
func windowMode() -> String {
let screenRect = UIScreen.main.bounds
let appRect = UIApplication.shared.windows[0].bounds
if (UIDevice.current.userInterfaceIdiom == .phone) {
return "iPhone fullscreen"
} else if (screenRect == appRect) {
return "iPad fullscreen"
} else if (appRect.size.height < screenRect.size.height) {
return "iPad slide over"
} else {
return "iPad split view"
}
}
在 iOS 9 中,是否可以在 iOS 9 的侧拉或拆分视图模式下检测应用何时处于 运行?
我已经尝试通读 Apple 的 documentation 关于 iOS 9 多任务处理,但没有任何运气…
我问是因为我的应用程序中可能有一个功能,当应用程序在侧拉中打开时我想禁用该功能。
横向尺寸 class 在滑过或 33% 拆分视图时会更紧凑。不过,我认为一旦达到 50% 或 66% 就无法检测到。
您可以同时查看 -willTransitionToTraitCollection:withTransitionCoordinator: 的大小 class 和 viewWillTransitionToSize:withTransitionCoordinator: 的 CGSize。不过不建议对大小值进行硬编码。
经过多次 'tinkering',我为我的应用程序找到了一个可能适合您的解决方案:
在 AppDelegate.swift 中,创建以下变量:
var slideOverActive: Bool = false
然后,在所有视图控制器中,将 UIApplicationDelegate 添加到 Class 定义中,创建一个 appDelegate 变量,然后添加以下 traitCollectionDidChange 函数:
class myViewController: UIViewController, UIApplicationDelegate {
var appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
let screenWidth = UIScreen.mainScreen().bounds.width
if previousTraitCollection != nil {
let horizontalSizeClass: Int = previousTraitCollection!.horizontalSizeClass.rawValue
if screenWidth == 1024 || screenWidth == 768 { // iPad
if horizontalSizeClass == 2 { // Slide Over is ACTIVE!
appDelegate.slideOverActive = true
} else {
appDelegate.slideOverActive = false
}
}
}
}
}
然后,在您的代码中任何您想检查滑盖是否处于活动状态的地方,只需检查:
if appDelegate.slideOverActive == true {
// DO THIS
} else {
// DO THIS
}
这有点变通,但目前对我有用。
旅途愉快!
只需检查您的 window 是否占据了整个屏幕:
BOOL isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
如果这是错误的,那么您 运行 处于拆分视图或幻灯片中。
这里是代码片段,无论轮换如何,它都会自动维护这个标志
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
我来晚了,但如果你想要一个独立于方向的 属性,试试这个:
extension UIApplication
{
func isRunningInFullScreen() -> Bool
{
if let w = self.keyWindow
{
let maxScreenSize = max(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let minScreenSize = min(UIScreen.mainScreen().bounds.size.width, UIScreen.mainScreen().bounds.size.height)
let maxAppSize = max(w.bounds.size.width, w.bounds.size.height)
let minAppSize = min(w.bounds.size.width, w.bounds.size.height)
return maxScreenSize == maxAppSize && minScreenSize == minAppSize
}
return true
}
}
而且我真的迟到了!但是,尽管如此,这里有一个简单、快速的解决问题的方法。使用 let width = UIScreen.mainScreen().applicationFrame.size.width
我们可以检测我的应用程序 window 的宽度,然后当它小于某个数字时(即在 iPhone 屏幕上或在拆分视图中)会发生一些事情,对让不同的事情发生在较小的屏幕上。为了让计算机一遍又一遍地检查宽度,我们可以 运行 每百分之一秒一个 NSTimer ,然后如果宽度是 higher/lower 就做一些事情。
一些给你的测量值(你必须决定让东西出现的宽度above/below):
iPhone 6S Plus: 414.0mm
iPhone 6S: 375.0mm
iPhone 5S: 320.0mm
iPad(纵向):768.0mm
iPad(1/3分割视图):320.0mm
iPad Air 2(1/2分割视图):507.0mm
iPad(横向):1024.0mm
这是一个代码片段:
class ViewController: UIViewController {
var widthtimer = NSTimer()
func checkwidth() {
var width = UIScreen.mainScreen().applicationFrame.size.width
if width < 507 { // The code inside this if statement will occur if the width is below 507.0mm (on portrait iPhones and in iPad 1/3 split view only). Use the measurements provided in the Stack Overflow answer above to determine at what width to have this occur.
// do the thing that happens in split view
textlabel.hidden = false
} else if width > 506 {
// undo the thing that happens in split view when return to full-screen
textlabel.hidden = true
}
}
override func viewDidAppear(animated: Bool) {
widthtimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "checkwidth", userInfo: nil, repeats: true)
// runs every hundredth of a second to call the checkwidth function, to check the width of the window.
}
override func viewDidDisappear(animated: Bool) {
widthtimer.invalidate()
}
}
希望对前来偷看的人有所帮助!
我最近必须确定基于应用程序的显示样式,不仅包括它是否更改为拆分视图或滑过,还包括应用程序使用了屏幕的哪一部分(完整,1/3 , 1/2, 2/3).将其添加到 ViewController 子类能够解决问题。
/// Dismisses this ViewController with animation from a modal state.
func dismissFormSheet () {
dismissViewControllerAnimated(true, completion: nil)
}
private func deviceOrientation () -> UIDeviceOrientation {
return UIDevice.currentDevice().orientation
}
private func getScreenSize () -> (description:String, size:CGRect) {
let size = UIScreen.mainScreen().bounds
let str = "SCREEN SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
private func getApplicationSize () -> (description:String, size:CGRect) {
let size = UIApplication.sharedApplication().windows[0].bounds
let str = "\n\nAPPLICATION SIZE:\nwidth: \(size.width)\nheight: \(size.height)"
return (str, size)
}
func respondToSizeChange (layoutStyle:LayoutStyle) {
// Respond accordingly to the change in size.
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
private func determineLayout () -> LayoutStyle {
if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
return .iPadFullscreen
}
// Set a range in case there is some mathematical inconsistency or other outside influence that results in the application width being less than exactly 1/3, 1/2 or 2/3.
let lowRange = screenWidth - 15
let highRange = screenWidth + 15
if lowRange / 2 <= appWidth && appWidth <= highRange / 2 {
return .iPadHalfScreen
} else if appWidth <= highRange / 3 {
return .iPadOneThirdScreen
} else {
return .iPadTwoThirdScreeen
}
}
override func traitCollectionDidChange(previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
respondToSizeChange(determineLayout())
}
添加到@Tamas 的回答:
这是无论轮换如何自动维护此标志的代码片段。
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
{
// simply create a property of 'BOOL' type
isRunningInFullScreen = CGRectEqualToRect([UIApplication sharedApplication].delegate.window.frame, [UIApplication sharedApplication].delegate.window.screen.bounds);
}
重新打包所有这些的另一种方式
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !window.frame.equalTo(window.screen.bounds)
}
}
那你就可以
UIApplication.shared.isSplitOrSlideOver
在 SwiftUIApplication.sharedApplication.isSplitOrSlideOver
在 Objective-C
请注意,在 Swift 中,window
对象是双可选的...
对于 iOS 13+(注意,我还没有自己测试 iOS 13 代码)
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let window = self.windows.filter({ [=11=].isKeyWindow }).first else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
正在尝试 [UIScreen mainScreen].bounds, self.window.screen.bounds, self.window.frame, UIApplication.sharedApplication.keyWindow.frame 等等,唯一可行的解决方案是弃用方法
CGRect frame = [UIScreen mainScreen].applicationFrame;
我是这样修复的
CGRect frame = [UIScreen mainScreen].applicationFrame;
frame = CGRectMake(0, 0, frame.size.width + frame.origin.x, frame.size.height + frame.origin.y);
self.window.frame = frame;
类似于 Dan Rosenstark 的解决方案,但更改为在新的 iPad Pro 上工作,它似乎报告了不同的框架和 screen.bounds 高度,具体取决于它是否直接 运行设备通过 Xcode,或者通过 TestFlight 或 App Store 编译发布。通过 AS 或 TF 时,高度将 return 980,而不是通过 Xcode 应该喜欢的 1024,导致它不可能 return true。
extension UIApplication {
public var isSplitOrSlideOver: Bool {
guard let w = self.delegate?.window, let window = w else { return false }
return !(window.frame.width == window.screen.bounds.width)
}
}
通过使用下面的代码,您可以检查 splitViewController 是否已折叠
if splitViewController?.isCollapsed ?? false {
// splitview collapsed
} else {
// splitview not collapsed
}
我对@Michael Voccola 解决方案进行了编辑,解决了方向问题
我在我的情况下使用这种方式来检测所有 iPad 分屏状态和处理布局
只需调用 determineLayout()
即可获取当前 layoutStyle
private func getScreenSize() -> CGRect {
let size = UIScreen.main.bounds
return size
}
private func getApplicationSize() -> CGRect {
let size = UIApplication.shared.windows[0].bounds
return size
}
enum LayoutStyle: String {
case iPadFullscreen = "iPad Full Screen"
case iPadHalfScreen = "iPad 1/2 Screen"
case iPadTwoThirdScreeen = "iPad 2/3 Screen"
case iPadOneThirdScreen = "iPad 1/3 Screen"
case iPhoneFullScreen = "iPhone"
}
func determineLayout() -> LayoutStyle {
if UIDevice.current.userInterfaceIdiom == .phone {
return .iPhoneFullScreen
}
let screenSize = getScreenSize().size
let appSize = getApplicationSize().size
let screenWidth = screenSize.width
let appWidth = appSize.width
if screenSize == appSize {
// full screen
return .iPadFullscreen
}
let persent = CGFloat(appWidth / screenWidth) * 100.0
if persent <= 55.0 && persent >= 45.0 {
// The view persent between 45-55 that's mean it's half screen
return .iPadHalfScreen
} else if persent > 55.0 {
// more than 55% that's mean it's 2/3
return .iPadTwoThirdScreeen
} else {
// less than 45% it's 1/3
return .iPadOneThirdScreen
}
}
这是我在 iPhone/iPad iOS 应用程序中使用的没有常量的更简单且不那么脆弱的方法。
此代码还区分滑过和拆分视图。
为清楚起见,我在此处返回字符串值,您可以随意使用枚举值并根据您的应用合并全屏的两种情况。
func windowMode() -> String {
let screenRect = UIScreen.main.bounds
let appRect = UIApplication.shared.windows[0].bounds
if (UIDevice.current.userInterfaceIdiom == .phone) {
return "iPhone fullscreen"
} else if (screenRect == appRect) {
return "iPad fullscreen"
} else if (appRect.size.height < screenRect.size.height) {
return "iPad slide over"
} else {
return "iPad split view"
}
}