Skip to content

Commit 2f8de41

Browse files
committed
Fix SwiftKickMobile#185 incorrect margin adjustments in landscape
1 parent c89cadc commit 2f8de41

8 files changed

+60
-35
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Change Log
22
All notable changes to this project will be documented in this file.
33

4+
## 4.1.3
5+
### Bug Fixes
6+
* Fix #185 Incorrect margin adjustments in landscape
7+
48
## 4.1.2
59

610
### Features

SwiftMessages.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
Pod::Spec.new do |spec|
22
spec.name = 'SwiftMessages'
3-
spec.version = '4.1.2'
3+
spec.version = '4.1.3'
44
spec.license = { :type => 'MIT' }
55
spec.homepage = 'https://github.com/SwiftKickMobile/SwiftMessages'
66
spec.authors = { 'Timothy Moose' => 'tim@swiftkick.it' }
77
spec.summary = 'A very flexible message bar for iOS written in Swift.'
8-
spec.source = {:git => 'https://github.com/SwiftKickMobile/SwiftMessages.git', :tag => '4.1.2'}
8+
spec.source = {:git => 'https://github.com/SwiftKickMobile/SwiftMessages.git', :tag => '4.1.3'}
99
spec.platform = :ios, '8.0'
1010
spec.ios.deployment_target = '8.0'
1111
spec.source_files = 'SwiftMessages/**/*.swift'

SwiftMessages/Animator.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ public protocol AnimationDelegate: class {
1717
}
1818

1919
/**
20-
An option set that represents the known types of safe zone conflicts
21-
that may need custom margin adustments.
20+
An option set representing the known types of safe area conflicts
21+
that could require margin adustments on the message view in order to
22+
get the layouts to look right.
2223
*/
2324
public struct SafeZoneConflicts: OptionSet {
2425
public let rawValue: Int
@@ -36,11 +37,10 @@ public struct SafeZoneConflicts: OptionSet {
3637
/// Message view behind home indicator on iPhone X
3738
public static let homeIndicator = SafeZoneConflicts(rawValue: 1 << 2)
3839

39-
/// Message view covering status bar on iPhone 8 or lower. One would expect the
40-
/// top safe zone to be 0, but in the current version of iOS 11, it is non-zero (20),
41-
/// which SwiftMessages will automatically compensate for by subtracting that amount
42-
/// form the layout margins.
43-
public static let coveredStatusBar = SafeZoneConflicts(rawValue: 1 << 3)
40+
/// Message view is over the status bar on an iPhone 8 or lower. This is a special
41+
/// case because we logically expect the top safe area to be zero, but it is reported as 20
42+
/// (which seems like an iOS bug). We use the `overStatusBar` to indicate this special case.
43+
public static let overStatusBar = SafeZoneConflicts(rawValue: 1 << 3)
4444
}
4545

4646
public class AnimationContext {

SwiftMessages/MarginAdjustable+Animation.swift

+18-8
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,19 @@ public extension MarginAdjustable where Self: UIView {
1414
var top: CGFloat = 0
1515
top += bounceAnimationOffset
1616
if !context.safeZoneConflicts.isDisjoint(with: [.sensorNotch, .statusBar]) {
17-
if #available(iOS 11, *), container.safeAreaInsets.top > 0 {
18-
// Linear formula based on:
19-
// iPhone 8 - 20pt top safe area with 0pt adjustment
20-
// iPhone X - 44pt top safe area with -6pt adjustment
21-
top -= 6 * (container.safeAreaInsets.top - 20) / (44 - 20)
17+
if #available(iOS 11, *) {
18+
do {
19+
// To accommodate future safe areas, using a linear formula based on
20+
// two data points:
21+
// iPhone 8 - 20pt top safe area needs 0pt adjustment
22+
// iPhone X - 44pt top safe area needs -6pt adjustment
23+
top -= 6 * (container.safeAreaInsets.top - 20) / (44 - 20)
24+
}
2225
top += safeAreaTopOffset
23-
} else {
26+
} else if UIApplication.shared.statusBarOrientation == .portrait || UIApplication.shared.statusBarOrientation == .portraitUpsideDown {
2427
top += statusBarOffset
2528
}
26-
}
27-
if #available(iOS 11, *), !context.safeZoneConflicts.isDisjoint(with: .coveredStatusBar) {
29+
} else if #available(iOS 11, *), !context.safeZoneConflicts.isDisjoint(with: .overStatusBar) {
2830
top -= safeAreaInsets.top
2931
}
3032
return top
@@ -35,6 +37,14 @@ public extension MarginAdjustable where Self: UIView {
3537
bottom += bounceAnimationOffset
3638
if !context.safeZoneConflicts.isDisjoint(with: [.homeIndicator]) {
3739
if #available(iOS 11, *), container.safeAreaInsets.bottom > 0 {
40+
do {
41+
// This adjustment was added to fix a layout issue with iPhone X in
42+
// landscape mode. Using a linear formula based on two data points to help
43+
// future proof against future safe areas:
44+
// iPhone X portrait: 34pt bottom safe area needs 0pt adjustment
45+
// iPhone X landscape: 21pt bottom safe area needs 12pt adjustment
46+
bottom -= 12 * (container.safeAreaInsets.bottom - 34) / (34 - 21)
47+
}
3848
bottom += safeAreaBottomOffset
3949
}
4050
}

SwiftMessages/PhysicsAnimation.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,10 @@ public class PhysicsAnimation: NSObject, Animator {
9999
}
100100
adjustable.preservesSuperviewLayoutMargins = false
101101
if #available(iOS 11, *) {
102-
var margins = adjustable.directionalLayoutMargins
102+
var margins = adjustable.safeAreaInsets
103103
margins.top = top
104104
margins.bottom = bottom
105-
adjustable.directionalLayoutMargins = margins
105+
adjustable.layoutMargins = margins
106106
} else {
107107
var margins = adjustable.layoutMargins
108108
margins.top = top

SwiftMessages/Presenter.swift

+20-11
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,12 @@ class Presenter: NSObject {
219219

220220
private func safeZoneConflicts() -> SafeZoneConflicts {
221221
guard let window = maskingView.window else { return [] }
222-
let inNormalWindowLevel: Bool = {
222+
let windowLevel: UIWindowLevel = {
223223
if let vc = presentationContext.viewControllerValue() as? WindowViewController {
224-
return vc.windowLevel == UIWindowLevelNormal
224+
return vc.windowLevel
225225
}
226-
return true
227-
} ()
226+
return UIWindowLevelNormal
227+
}()
228228
// TODO `underNavigationBar` and `underTabBar` should look up the presentation context's hierarchy
229229
// TODO for cases where both should be true (probably not an issue for typical height messages, though).
230230
let underNavigationBar: Bool = {
@@ -236,15 +236,24 @@ class Presenter: NSObject {
236236
return false
237237
}()
238238
if #available(iOS 11, *) {
239-
if !inNormalWindowLevel {
239+
if windowLevel > UIWindowLevelNormal {
240240
// TODO seeing `maskingView.safeAreaInsets.top` value of 20 on
241-
// iPhone 8 with status bar window level, which doesn't seem right
242-
// since the status bar is covered by the window. Applying a special rule
241+
// iPhone 8 with status bar window level. This seems like an iOS bug since
242+
// the message view's window is above the status bar. Applying a special rule
243243
// to allow the animator to revove this amount from the layout margins if needed.
244-
if maskingView.safeAreaInsets.top <= 20 {
245-
return [.coveredStatusBar]
244+
// This may need to be reworked if any future device has a legitimate 20pt top safe area,
245+
// such as with a potentially smaller notch.
246+
if maskingView.safeAreaInsets.top == 20 {
247+
return [.overStatusBar]
246248
} else {
247-
return [.sensorNotch, .homeIndicator]
249+
var conflicts: SafeZoneConflicts = []
250+
if maskingView.safeAreaInsets.top > 0 {
251+
conflicts.formUnion(.sensorNotch)
252+
}
253+
if maskingView.safeAreaInsets.bottom > 0 {
254+
conflicts.formUnion(.homeIndicator)
255+
}
256+
return conflicts
248257
}
249258
}
250259
var conflicts: SafeZoneConflicts = []
@@ -257,7 +266,7 @@ class Presenter: NSObject {
257266
return conflicts
258267
} else {
259268
if UIApplication.shared.isStatusBarHidden { return [] }
260-
if !inNormalWindowLevel || underNavigationBar { return [] }
269+
if (windowLevel > UIWindowLevelNormal) || underNavigationBar { return [] }
261270
let statusBarFrame = UIApplication.shared.statusBarFrame
262271
let statusBarWindowFrame = window.convert(statusBarFrame, from: nil)
263272
let statusBarViewFrame = maskingView.convert(statusBarWindowFrame, from: nil)

SwiftMessages/Resources/StatusLine.xib

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33
<device id="retina4_7" orientation="portrait">
44
<adaptation id="fullscreen"/>
55
</device>
66
<dependencies>
7-
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
7+
<deployment version="2304" identifier="iOS"/>
8+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
99
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
10+
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
1011
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1112
</dependencies>
1213
<objects>
@@ -37,6 +38,7 @@
3738
<constraint firstAttribute="trailingMargin" secondItem="Jtv-Px-mvN" secondAttribute="trailing" constant="30" id="hII-4c-bJX"/>
3839
</constraints>
3940
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
41+
<viewLayoutGuide key="safeArea" id="yBK-G0-nSP"/>
4042
<userDefinedRuntimeAttributes>
4143
<userDefinedRuntimeAttribute type="number" keyPath="statusBarOffset">
4244
<real key="value" value="14"/>

SwiftMessages/TopBottomAnimation.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,10 @@ public class TopBottomAnimation: NSObject, Animator {
110110
}
111111
adjustable.preservesSuperviewLayoutMargins = false
112112
if #available(iOS 11, *) {
113-
var margins = adjustable.directionalLayoutMargins
113+
var margins = adjustable.safeAreaInsets
114114
margins.top = top
115115
margins.bottom = bottom
116-
adjustable.directionalLayoutMargins = margins
116+
adjustable.layoutMargins = margins
117117
} else {
118118
var margins = adjustable.layoutMargins
119119
margins.top = top

0 commit comments

Comments
 (0)