Skip to content

Commit 1b0966b

Browse files
author
Luc Dion
authored
Merge pull request #85 from mirego/add_safeAreaInsets
Examples now support iOS 11 and iPhone X landscape mode
2 parents a25da84 + 6013e94 commit 1b0966b

24 files changed

+157
-103
lines changed

.travis.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
language: objective-c
2-
osx_image: xcode8.3
2+
osx_image: xcode9
33

44
env:
55
matrix:
6+
- SCHEME=PinLayout SDK=iphonesimulator11.0
67
- SCHEME=PinLayout SDK=iphonesimulator10.3
7-
- SCHEME=PinLayoutSample SDK=iphonesimulator10.3
8-
- SCHEME=PinLayoutTVOS SDK=appletvsimulator10.2
8+
- SCHEME=PinLayoutSample SDK=iphonesimulator11.0
9+
- SCHEME=PinLayoutTVOS SDK=appletvsimulator11.0
910

1011
before_install:
1112
# - brew outdated xctool || brew upgrade xctool;

Example/PinLayoutSample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
246813001F8D013500462E53 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 246812FF1F8D013500462E53 /* TodayViewController.swift */; };
2323
246813031F8D013500462E53 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 246813011F8D013500462E53 /* MainInterface.storyboard */; };
2424
246813071F8D013500462E53 /* PinLayoutTodayExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 246812FB1F8D013500462E53 /* PinLayoutTodayExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
25+
247157941F87BD680003424F /* UIEdgeInsets+PinLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 247157931F87BD680003424F /* UIEdgeInsets+PinLayout.swift */; };
2526
249326891EEEEE3D00BCB814 /* Stylesheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 249326881EEEEE3D00BCB814 /* Stylesheet.swift */; };
2627
2493268C1EEEEFF100BCB814 /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2493268B1EEEEFF100BCB814 /* BaseViewController.swift */; };
2728
2493268E1EEEF02700BCB814 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2493268D1EEEF02700BCB814 /* BaseView.swift */; };
@@ -128,6 +129,7 @@
128129
246812FF1F8D013500462E53 /* TodayViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayViewController.swift; sourceTree = "<group>"; };
129130
246813021F8D013500462E53 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
130131
246813041F8D013500462E53 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
132+
247157931F87BD680003424F /* UIEdgeInsets+PinLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+PinLayout.swift"; sourceTree = "<group>"; };
131133
249326881EEEEE3D00BCB814 /* Stylesheet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stylesheet.swift; sourceTree = "<group>"; };
132134
2493268B1EEEEFF100BCB814 /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = "<group>"; };
133135
2493268D1EEEF02700BCB814 /* BaseView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = "<group>"; };
@@ -244,6 +246,7 @@
244246
2493268B1EEEEFF100BCB814 /* BaseViewController.swift */,
245247
2439CC381E665C6B003326FB /* BasicView.swift */,
246248
249326881EEEEE3D00BCB814 /* Stylesheet.swift */,
249+
247157931F87BD680003424F /* UIEdgeInsets+PinLayout.swift */,
247250
);
248251
path = Common;
249252
sourceTree = "<group>";
@@ -628,6 +631,7 @@
628631
241637741F8E4BC200EE703A /* IntroObjectiveCViewController.m in Sources */,
629632
2439CC541E665C6B003326FB /* RelativeView.swift in Sources */,
630633
2439CC551E665C6B003326FB /* RelativeViewController.swift in Sources */,
634+
247157941F87BD680003424F /* UIEdgeInsets+PinLayout.swift in Sources */,
631635
24D18D1E1F3DED0D008129EF /* IntroRTLViewController.swift in Sources */,
632636
24A9C2031EF16A3E00F2CF64 /* AutoAdjustingSizeView.swift in Sources */,
633637
24D18D1D1F3DED0D008129EF /* IntroRTLView.swift in Sources */,

Example/PinLayoutSample/UI/Common/BaseFormView.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ class BaseFormView: BaseView {
4646
override func layoutSubviews() {
4747
super.layoutSubviews()
4848

49-
formScrollView.pin.topLeft().bottomRight()
49+
formScrollView.pin.top().left().bottom().right().margin(safeArea)
5050
}
5151

52-
override func didChangeLayoutGuides() {
53-
super.didChangeLayoutGuides()
54-
formScrollView.contentOffset = CGPoint(x: 0, y: topLayoutGuide)
52+
override func safeAreaDidChange() {
53+
super.safeAreaDidChange()
54+
formScrollView.contentOffset = CGPoint(x: 0, y: safeArea.top)
5555
}
5656

5757
@objc

Example/PinLayoutSample/UI/Common/BaseView.swift

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@
2020
import UIKit
2121

2222
class BaseView: UIView {
23-
fileprivate (set) var topLayoutGuide: CGFloat = 0
24-
fileprivate (set) var bottomLayoutGuide: CGFloat = 0
23+
private var legacySafeArea: UIEdgeInsets = .zero
24+
25+
// safeArea returns UIView.safeAreaInsets for iOS 11+ or
26+
// an UIEdgeInsets initialized with the UIViewController's topLayoutGuide and
27+
// bottomLayoutGuide for other iOS versions.
28+
var safeArea: UIEdgeInsets {
29+
if #available(iOS 11.0, *) {
30+
return safeAreaInsets
31+
} else {
32+
return legacySafeArea
33+
}
34+
}
2535

2636
init() {
2737
super.init(frame: .zero)
@@ -32,25 +42,17 @@ class BaseView: UIView {
3242
fatalError("init(coder:) has not been implemented")
3343
}
3444

35-
func setLayoutGuides(top: CGFloat, bottom: CGFloat) {
36-
var didChange = false
37-
38-
if top != topLayoutGuide {
39-
topLayoutGuide = top
40-
didChange = true
41-
}
42-
43-
if bottom != bottomLayoutGuide {
44-
bottomLayoutGuide = bottom
45-
didChange = true
46-
}
47-
48-
if didChange {
49-
didChangeLayoutGuides()
50-
}
45+
func setSafeArea(_ safeArea: UIEdgeInsets) {
46+
guard safeArea != self.safeArea else { return }
47+
legacySafeArea = safeArea
48+
safeAreaDidChange()
5149
}
5250

53-
func didChangeLayoutGuides() {
51+
func safeAreaDidChange() {
5452
setNeedsLayout()
5553
}
54+
55+
override func safeAreaInsetsDidChange() {
56+
safeAreaDidChange()
57+
}
5658
}

Example/PinLayoutSample/UI/Common/BaseViewController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
import UIKit
2121

2222
class BaseViewController: UIViewController {
23-
2423
override func viewWillLayoutSubviews() {
2524
super.viewWillLayoutSubviews()
2625

27-
if let view = view as? BaseView {
28-
view.setLayoutGuides(top: topLayoutGuide.length, bottom: bottomLayoutGuide.length)
26+
if #available(iOS 11.0, *) {
27+
} else if let view = view as? BaseView {
28+
view.setSafeArea(UIEdgeInsets(top: topLayoutGuide.length, left: 0, bottom: bottomLayoutGuide.length, right: 0))
2929
}
3030
}
3131
}

Example/PinLayoutSample/UI/Common/BasicView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class BasicView: UIView {
4343
override func layoutSubviews() {
4444
super.layoutSubviews()
4545

46-
label.pin.topLeft().right().margin(4).fitSize()
46+
label.pin.top().left().right().margin(4).fitSize()
4747
}
4848

4949
var sizeThatFitsExpectedArea: CGFloat = 40 * 40
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// UIEdgeInsets+PinLayout.swift
3+
// PinLayoutSample
4+
//
5+
// Created by DION, Luc (MTL) on 2017-10-06.
6+
// Copyright © 2017 Mirego. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension UIEdgeInsets {
12+
func insetBy(dx: CGFloat, dy: CGFloat) -> UIEdgeInsets {
13+
return UIEdgeInsets(top: self.top + dy, left: self.left + dx, bottom: self.bottom + dy, right: self.right + dx)
14+
}
15+
16+
func minInsets(_ insets: UIEdgeInsets) -> UIEdgeInsets {
17+
return UIEdgeInsets(top: minValue(self.top, minValue: insets.top),
18+
left: minValue(self.left, minValue: insets.left),
19+
bottom: minValue(self.bottom, minValue: insets.bottom),
20+
right: minValue(self.right, minValue: insets.right))
21+
}
22+
23+
func minInsets(dx: CGFloat, dy: CGFloat) -> UIEdgeInsets {
24+
return UIEdgeInsets(top: minValue(self.top, minValue: dy),
25+
left: minValue(self.left, minValue: dx),
26+
bottom: minValue(self.bottom, minValue: dy),
27+
right: minValue(self.right, minValue: dx))
28+
}
29+
30+
fileprivate func minValue(_ value: CGFloat, minValue: CGFloat) -> CGFloat {
31+
return value >= minValue ? value : minValue
32+
}
33+
}

Example/PinLayoutSample/UI/Examples/AdjustToContainer/AdjustToContainerView.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@ import UIKit
2121
import PinLayout
2222

2323
class AdjustToContainerView: BaseView {
24-
24+
fileprivate let contentView = UIView()
2525
fileprivate let languageSelectorView = ChoiceSelectorView(text: "What is your favorite language?", choices: ["Swift", "Objective-C", "C++"])
2626
fileprivate let swiftOpinionSelectorView = ChoiceSelectorView(text: "Overall, are you satisfied with the Swift performance in your projects?", choices: ["Yes", "No"])
2727
fileprivate let swiftUsageSelectorView = ChoiceSelectorView(text: "How often do you typically use Swift?", choices: ["Daily", "Weekly", "Montly", "Do not use"])
2828

2929
override init() {
3030
super.init()
3131

32-
addSubview(languageSelectorView)
33-
addSubview(swiftOpinionSelectorView)
34-
addSubview(swiftUsageSelectorView)
32+
addSubview(contentView)
33+
34+
contentView.addSubview(languageSelectorView)
35+
contentView.addSubview(swiftOpinionSelectorView)
36+
contentView.addSubview(swiftUsageSelectorView)
3537
}
3638

3739
required init?(coder aDecoder: NSCoder) {
@@ -41,7 +43,10 @@ class AdjustToContainerView: BaseView {
4143
override func layoutSubviews() {
4244
super.layoutSubviews()
4345

44-
languageSelectorView.pin.top(topLayoutGuide).left().right().fitSize()
46+
// Layout the contentView using the view's safeArea.
47+
contentView.pin.top().bottom().left().right().margin(safeArea)
48+
49+
languageSelectorView.pin.top().left().right().fitSize()
4550
swiftOpinionSelectorView.pin.below(of: languageSelectorView, aligned: .left).right().marginTop(10).fitSize()
4651
swiftUsageSelectorView.pin.below(of: swiftOpinionSelectorView, aligned: .left).right().marginTop(10).fitSize()
4752
}

Example/PinLayoutSample/UI/Examples/AdjustToContainer/Subviews/ChoiceSelectorView.swift

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,30 @@ class ChoiceSelectorView: UIView {
4646

4747
override func layoutSubviews() {
4848
super.layoutSubviews()
49-
_ = layout(size: frame.size)
49+
_ = layout()
5050
}
5151

5252
override func sizeThatFits(_ size: CGSize) -> CGSize {
53-
return layout(size: size)
53+
// 1) Set the width to the specified width
54+
self.pin.width(size.width)
55+
56+
// 2) Layout the contentView's controls
57+
return layout()
5458
}
5559

56-
fileprivate func layout(size: CGSize) -> CGSize {
57-
let width = size.width
60+
fileprivate func layout() -> CGSize {
5861
let margin: CGFloat = 12
5962

6063
if frame.width > 500 {
6164
// The UISegmentedControl is at the top-right corner and the label takes the remaining horizontal space.
62-
segmentedControl.pin.topRight().margin(margin)
65+
segmentedControl.pin.top().right().margin(margin)
6366
textLabel.pin.top().left().left(of: segmentedControl).margin(margin).fitSize()
6467
} else {
6568
// The UISegmentedControl is placed below the label.
6669
textLabel.pin.top().left().right().margin(margin).fitSize()
6770
segmentedControl.pin.below(of: textLabel).right().margin(margin)
6871
}
6972

70-
return CGSize(width: width, height: max(textLabel.frame.maxY, segmentedControl.frame.maxY) + margin)
73+
return CGSize(width: frame.width, height: max(textLabel.frame.maxY, segmentedControl.frame.maxY) + margin)
7174
}
7275
}

Example/PinLayoutSample/UI/Examples/AutoAdjustingSizeView/AutoAdjustingSizeView.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,27 @@ class AutoAdjustingSizeView: BaseView {
7474
override func layoutSubviews() {
7575
super.layoutSubviews()
7676

77-
contentScrollView.pin.topLeft().bottomRight()
77+
// Layout the contentScrollView using the view's safeArea.
78+
contentScrollView.pin.top().bottom().left().right().margin(safeArea)
7879

7980
row1.pin.top().left().right().height(40)
8081
row1Item1.pin.top().left().bottom().width(50).margin(2)
8182
row1Item2.pin.right(of: row1Item1, aligned: .top).bottomRight().margin(0, 2, 2, 2)
8283

8384
row2.pin.below(of: row1, aligned: .left).size(of: row1).marginTop(10)
8485
row2Item1.pin.top().right().bottom().width(150).width(25%).margin(2)
85-
row2Item2.pin.left(of: row2Item1, aligned: .top).bottomLeft().margin(0, 2, 2, 2)
86+
row2Item2.pin.left(of: row2Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2)
8687

8788
row3.pin.below(of: row2, aligned: .left).size(of: row1).marginTop(10)
8889
row3Item1.pin.topCenter().width(50).bottom().margin(2)
89-
row3Item2.pin.left(of: row3Item1, aligned: .top).bottomLeft().margin(0, 2, 2, 2)
90-
row3Item3.pin.right(of: row3Item1, aligned: .top).bottomRight().margin(0, 2, 2, 2)
90+
row3Item2.pin.left(of: row3Item1, aligned: .top).left().bottom().margin(0, 2, 2, 2)
91+
row3Item3.pin.right(of: row3Item1, aligned: .top).right().bottom().margin(0, 2, 2, 2)
9192

9293
row4.pin.below(of: row3, aligned: .left).size(of: row1).marginTop(10)
9394
row4Item1.pin.top().left().width(25%).bottom().margin(2)
9495
row4Item2.pin.right(of: row4Item1, aligned: .top).width(50%).bottom().margin(0, 2, 2, 2)
95-
row4Item3.pin.right(of: row4Item2, aligned: .top).bottomRight().margin(0, 2, 2, 2)
96+
row4Item3.pin.right(of: row4Item2, aligned: .top).right().bottom().margin(0, 2, 2, 2)
9697

97-
contentScrollView.contentSize = CGSize(width: frame.width, height: row4.frame.maxY)
98+
contentScrollView.contentSize = CGSize(width: contentScrollView.frame.width, height: row4.frame.maxY)
9899
}
99100
}

0 commit comments

Comments
 (0)