检测应用程序是否在 iOS 9 中以侧拉或分屏浏览模式运行


在iOS 9

中,是否可以检测应用程序何时在iOS 9的侧滑或拆分浏览模式下运行?

我尝试通读苹果关于iOS 9多任务处理的文档,但没有任何运气......

我问是因为我的应用程序中可能有一个功能,我想在侧拉中打开应用程序时禁用该功能。

只需检查您的窗口是否占据了整个屏幕:

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 {
    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
  • UIApplication.sharedApplication.isSplitOrSlideOver 在 Objective-C 中

请注意,在 Swift 中,window 对象是双重可选的...跆拳道!

对于iOS 13+(注意,我还没有自己测试iOS 13代码(

extension UIApplication {
    public var isSplitOrSlideOver: Bool {
        guard let window = self.windows.filter({ $0.isKeyWindow }).first else { return false }
        return !(window.frame.width == window.screen.bounds.width)
    }
}

我来晚了,但是如果您想要一个独立于方向工作的属性,请尝试以下操作:

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
    }
}

最近必须确定应用程序的显示样式,不仅包括它是否更改为拆分视图或滑过,还包括屏幕的哪个部分用于应用程序(完整,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 = "nnAPPLICATION 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())
}

就像Dan Rosenstark的解决方案一样,但改为在新的iPad Pro上工作,这些iPad Pro似乎报告了不同的帧和屏幕高度,基于它是否通过Xcode直接在设备上运行,或者是否通过TestFlight或App Store编译和发布。当通过 AS 或 TF 时,高度将返回 980,而不是通过 Xcode 应该返回的 1024,导致无法返回 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)
    }
}

这是一种更简单,不那么脆弱的方法,没有常量,我在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"
  }
}

您可以同时观看 -willTransitionToTraitCollection:withTransitionCoordinator:用于大小类和 viewWillTransitionToSize:withTransitionCoordinator:用于视图的 CGSize。但是,不建议对大小值进行硬编码。

在滑动或 33% 拆分视图中,水平大小类将很紧凑。不过,我认为一旦达到 50% 或 66%,您就无法检测到。

我对@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
    }
}
extension UIApplication {
func isRunningInFullScreen() -> Bool {
    if let w = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .compactMap({$0 as? UIWindowScene})
        .first?.windows
        .filter({$0.isKeyWindow}).first {
            let maxScreenSize = max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height)
            let minScreenSize = min(UIScreen.main.bounds.size.width, UIScreen.main.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
    }
}

这是那些不想看到已弃用的keyWindow的快速lint投诉的人的代码

GitHub AbsoluteFrameDemo 上有使用 AppAbsoluteFrame 模块的项目。您可以使用它来检测应用的绝对帧。您还可以添加此扩展以获取应用程序多任务模式:

extension AppAbsoluteFrame {
    enum AppMode {
        case fullscreen, slideOver, splitScreen
        init(frame: CGRect, screenBounds: CGRect) {
            let appHasSameHeight = frame.height == screenBounds.height
            let appHasSameWidth = frame.width == screenBounds.width
            switch (appHasSameHeight, appHasSameWidth) {
            case (true, true):
                self = .fullscreen
            case (true, false):
                self = .splitScreen
            case (false, _):
                self = .slideOver
            }
        }
    }
    var appMode: AppMode {
        AppMode(frame: frame, screenBounds: screenBounds)
    }
}

该模块需要 UIWindowScene 来识别确切的窗口及其模式。

经过多次"修补",我为我的应用程序找到了一个可能适合您的解决方案:

在 AppDelegate.swift 中,创建以下变量:

var slideOverActive: Bool = false

然后,在所有视图控制器中,将 UIApplicationDelegate 添加到类定义中,创建一个 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
}

这是一种解决方法,但目前对我有用。

一路快乐!

补充@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);
}

Try [UIScreen mainScreen].bounds,self.window.screen.bounds,自.窗.帧,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;

而且我参加聚会真的迟到了!但尽管如此,这里有一个简单、快速的问题解决方案。使用let width = UIScreen.mainScreen().applicationFrame.size.width我们可以检测我的应用程序窗口的宽度,然后在它小于某个数字时(即在iPhone屏幕上或在拆分视图中(发生一些事情,这对于在较小的屏幕上发生不同的事情很有用。为了让计算机一遍又一遍地检查宽度,我们可以每隔百分之一秒运行一次 NSTimer,然后在宽度高于/低于某个值时执行操作。

为您提供一些测量(您必须决定使东西出现在上方/下方的宽度(:iPhone 6S Plus:414.0毫米
iPhone 6S:375.0毫米
iPhone 5S:320.0毫米
iPad(纵向(:768.0毫米
iPad(1/3分屏(:320.0毫米
iPad Air 2(1/2 分屏(:507.0 毫米
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()
}
}

我希望这可以帮助任何偷看的人!

通过使用下面的代码,您可以检查splitView控制器是否折叠

if splitViewController?.isCollapsed ?? false {
    // splitview collapsed
} else {
    // splitview not collapsed
 }

最新更新