From 0742cd96cbe6df018eace3c01c69dfbb72e85b13 Mon Sep 17 00:00:00 2001 From: Felix Mau Date: Thu, 23 Sep 2021 20:49:22 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20::=20Adapt=20notch=20size=20for=20i?= =?UTF-8?q?Phone=2013?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NotchGradientLoadingBarController.swift | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/GradientLoadingBar/Classes/NotchGradientLoadingBarController.swift b/GradientLoadingBar/Classes/NotchGradientLoadingBarController.swift index 97c17f3..889616e 100644 --- a/GradientLoadingBar/Classes/NotchGradientLoadingBarController.swift +++ b/GradientLoadingBar/Classes/NotchGradientLoadingBarController.swift @@ -16,15 +16,17 @@ open class NotchGradientLoadingBarController: GradientLoadingBarController { /// Values are based on /// + /// + /// Values for the iPhone 13 are based on testing in the simulator. private enum Config { /// The width of the iPhone notch. - static let notchWidth: CGFloat = 209 + static let notchWidth: CGFloat = UIDevice.isAnyIPhone13 ? 162 : 209 /// The radius of the small circle on the outside of the notch. static let smallCircleRadius: CGFloat = 6 /// The radius of the large circle on the inside of the notch. - static let largeCircleRadius: CGFloat = 20 + static let largeCircleRadius: CGFloat = 21 } // MARK: - Public methods @@ -50,26 +52,25 @@ open class NotchGradientLoadingBarController: GradientLoadingBarController { return } - // Our view will be masked therefore the view height needs to cover the notch-height plus the given user-height. - let height = 2 * Config.smallCircleRadius + Config.largeCircleRadius + self.height + // As we currently only support portrait mode (and no device rotation), we can safely use `bounds.size.width` here. + let screenWidth = superview.bounds.size.width + let notchBezierPath = self.notchBezierPath(for: screenWidth) NSLayoutConstraint.activate([ gradientActivityIndicatorView.topAnchor.constraint(equalTo: superview.topAnchor), - gradientActivityIndicatorView.heightAnchor.constraint(equalToConstant: height), + gradientActivityIndicatorView.heightAnchor.constraint(equalToConstant: notchBezierPath.bounds.height), gradientActivityIndicatorView.leadingAnchor.constraint(equalTo: superview.leadingAnchor), gradientActivityIndicatorView.trailingAnchor.constraint(equalTo: superview.trailingAnchor), ]) - // As we currently only support portrait mode (and no device rotation), we can safely use `bounds.size.width` here. - let screenWidth = superview.bounds.size.width - applyNotchMask(for: screenWidth) + maskView(with: notchBezierPath) } // MARK: - Private methods // swiftlint:disable:next function_body_length - private func applyNotchMask(for screenWidth: CGFloat) { + private func notchBezierPath(for screenWidth: CGFloat) -> UIBezierPath { // We always center the notch in the middle of the screen. let leftNotchPoint = (screenWidth - Config.notchWidth) / 2 + 0.5 let rightNotchPoint = (screenWidth + Config.notchWidth) / 2 @@ -96,7 +97,10 @@ open class NotchGradientLoadingBarController: GradientLoadingBarController { // We're moving the the large-circles a bit up. // This prevents having a straight line between the circles. // See - let verticalOffsetForLargeCircle: CGFloat = 3 + // + // But for the iPhone 13 we reduce this value: ‟iPhone 13 notch is 20% smaller in width, but it is also a little taller in height‟. + // See . + let verticalOffsetForLargeCircle: CGFloat = UIDevice.isAnyIPhone13 ? 1 : 3 // Draw the large circle right to the `leftNotchPoint`. bezierPath.addArc(withCenter: CGPoint(x: leftNotchPoint + Config.largeCircleRadius, @@ -196,6 +200,10 @@ open class NotchGradientLoadingBarController: GradientLoadingBarController { bezierPath.addLineTo(x: 0, y: height) bezierPath.close() + return bezierPath + } + + private func maskView(with bezierPath: UIBezierPath) { let shapeLayer = CAShapeLayer() shapeLayer.path = bezierPath.cgPath @@ -215,3 +223,31 @@ private extension UIBezierPath { addLine(to: CGPoint(x: x, y: y)) } } + +private extension UIDevice { + /// Starting from the iPhone 13 the notch is smaller, but a little bit higher. + /// Therefore we explicitly need to detect any iPhone 13 device. + /// + /// Based on: + /// Adapted for Simulator usage based on + static var isAnyIPhone13: Bool { + #if targetEnvironment(simulator) + let identifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "" + #else + var systemInfo = utsname() + uname(&systemInfo) + + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { + return identifier + } + + return identifier + String(UnicodeScalar(UInt8(value))) + } + #endif + + let iPhone13Identifiers = ["iPhone14,5", "iPhone14,2", "iPhone14,3"] + return iPhone13Identifiers.contains(identifier) + } +}