Skip to content

Commit 6c1ee69

Browse files
authored
New feature to extend a card's content under the title (#20)
* POC of extending card content below title * Generalise support of extended titles, by using new CardTitle * Allow opting in and out of ignoring the content inset * Fix a mismatch (though it breaks things) * Properly update on change of autoIgnoreContentInset * Simplify by using 0 inset when `autoIgnoreContentInset` and proper inset otherwise * Update GHA * Build using latest Xcode
1 parent 7b1350d commit 6c1ee69

21 files changed

+492
-90
lines changed

.github/workflows/swift.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ on:
88

99
jobs:
1010
build_xcode:
11-
runs-on: macos-11
11+
runs-on: macos-14
1212

1313
steps:
1414
- uses: maxim-lobanov/setup-xcode@v1
1515
with:
16-
xcode-version: '13.0' # latest-stable
16+
xcode-version: 15.3 # latest-stable
1717
- uses: actions/checkout@v2
1818
- name: Build TGCardVC
19-
run: xcodebuild -workspace . -scheme TGCardViewController -destination 'platform=iOS Simulator,name=iPhone 13'
19+
run: xcodebuild -workspace . -scheme TGCardViewController -destination 'platform=iOS Simulator,name=iPhone 14'
2020

2121
examples:
22-
runs-on: macos-11
22+
runs-on: macos-14
2323

2424
steps:
2525
- uses: maxim-lobanov/setup-xcode@v1
2626
with:
27-
xcode-version: '13.0' # latest-stable
27+
xcode-version: 15.3 # latest-stable
2828
- uses: actions/checkout@v2
2929
- name: Build Example
3030
run: |
3131
cd Example
32-
xcodebuild build -scheme 'Example' -destination 'platform=iOS Simulator,name=iPhone 13'
32+
xcodebuild build -scheme 'Example' -destination 'platform=iOS Simulator,name=iPhone 14'

Example/Example.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
archiveVersion = 1;
44
classes = {
55
};
6-
objectVersion = 52;
6+
objectVersion = 54;
77
objects = {
88

99
/* Begin PBXBuildFile section */
Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
{
22
"images" : [
33
{
4-
"idiom" : "universal",
5-
"scale" : "1x"
6-
},
7-
{
8-
"idiom" : "universal",
94
"filename" : "erlking.jpg",
10-
"scale" : "2x"
11-
},
12-
{
13-
"idiom" : "universal",
14-
"scale" : "3x"
5+
"idiom" : "universal"
156
}
167
],
178
"info" : {
18-
"version" : 1,
19-
"author" : "xcode"
9+
"author" : "xcode",
10+
"version" : 1
2011
},
2112
"properties" : {
2213
"template-rendering-intent" : "original"
2314
}
24-
}
15+
}

Example/cards/ExampleChildCard.swift

Lines changed: 141 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,160 @@
66
// Copyright © 2017 SkedGo Pty Ltd. All rights reserved.
77
//
88

9+
import SwiftUI
910
import UIKit
1011
import MapKit
1112

1213
import TGCardViewController
1314

15+
@available(iOS 16.0, *)
1416
class ExampleChildCard : TGPlainCard {
1517

18+
private let titleHandler: TitleHandler
19+
var contentHost: UIHostingController<ContentView>
20+
1621
init() {
17-
let content = ExampleChildContentView.instantiate()
22+
let handler = TitleHandler()
23+
self.titleHandler = handler
24+
25+
let contentHost = UIHostingController(rootView: ContentView())
26+
self.contentHost = contentHost
1827

19-
let accessoryLabel = UILabel()
20-
accessoryLabel.text = "This is an accessory view"
21-
accessoryLabel.textColor = .cyan
22-
accessoryLabel.textAlignment = .center
23-
accessoryLabel.sizeToFit()
28+
super.init(
29+
title: .customExtended(TitleView(handler: handler)),
30+
contentView: contentHost.view,
31+
mapManager: TGMapManager.sydney
32+
)
2433

25-
super.init(title: .default("Child", "With sticky button", accessoryLabel), contentView: content, mapManager: TGMapManager.sydney)
34+
handler.onClose = { [weak self] in
35+
self?.controller?.pop()
36+
}
2637

2738
self.topMapToolBarItems = [UIButton.dummyDetailDisclosureButton(), UIButton.dummyDetailDisclosureButton()]
2839
self.bottomMapToolBarItems = [] // This forces an empty bottom floating view
2940
}
3041

42+
override func shouldToggleSeparator(show: Bool, offset: CGFloat) -> Bool {
43+
titleHandler.scrollOffset = offset
44+
return false
45+
}
46+
47+
override func willAdjustContentAlpha(_ value: CGFloat) {
48+
titleHandler.contentAlpha = value
49+
}
50+
51+
}
52+
53+
private class TitleHandler: ObservableObject {
54+
var onClose: () -> Void = {}
55+
56+
@Published var scrollOffset: CGFloat = 0
57+
@Published var contentAlpha: CGFloat = 1
58+
59+
init() {}
60+
}
61+
62+
@available(iOS 16.0, *)
63+
struct TitleView: View {
64+
@ObservedObject fileprivate var handler: TitleHandler
65+
66+
var body: some View {
67+
HStack {
68+
VStack(alignment: .leading) {
69+
Text("A Child View")
70+
.font(.largeTitle)
71+
Text("With a subtitle")
72+
.font(.headline)
73+
}
74+
75+
Spacer()
76+
77+
Button {
78+
handler.onClose()
79+
} label: {
80+
Image(systemName: "xmark.circle.fill")
81+
}
82+
}
83+
.padding(.top, -10)
84+
.padding(.horizontal)
85+
.padding(.bottom)
86+
.foregroundColor(handler.contentAlpha < 1 ? .black : Color.init(white: 1.0 - fabs(handler.scrollOffset) / 20))
87+
// .foregroundStyle(.white)
88+
.background {
89+
Rectangle()
90+
// .foregroundStyle(.clear)
91+
// .overlay(
92+
// Gradient(stops: [
93+
// .init(color: .black.opacity(0.7), location: 0),
94+
// .init(color: .black.opacity(0), location: 0.3),
95+
// ])
96+
// )
97+
.foregroundStyle(.background)
98+
.opacity(handler.contentAlpha < 1 ? 1 : fabs(handler.scrollOffset) / 20)
99+
.offset(y: -20)
100+
.padding(.bottom, -16)
101+
}
102+
}
103+
}
104+
105+
@available(iOS 16.0, *)
106+
struct ContentView: View {
107+
var body: some View {
108+
VStack {
109+
Image("erlking")
110+
.resizable()
111+
.scaledToFit()
112+
.overlay(
113+
Gradient(stops: [
114+
.init(color: .black.opacity(0.7), location: 0),
115+
.init(color: .black.opacity(0), location: 0.3),
116+
.init(color: .black.opacity(0), location: 0.8),
117+
.init(color: .black.opacity(0.7), location: 1),
118+
])
119+
)
120+
121+
Text("""
122+
Who rides there so late through the night dark and drear?
123+
The father it is, with his infant so dear;
124+
He holdeth the boy tightly clasp'd in his arm,
125+
He holdeth him safely, he keepeth him warm.
126+
127+
"My son, wherefore seek'st thou thy face thus to hide?"
128+
"Look, father, the Erl-King is close by our side!
129+
Dost see not the Erl-King, with crown and with train?"
130+
"My son, 'tis the mist rising over the plain."
131+
132+
"Oh, come, thou dear infant! oh come thou with me!
133+
For many a game I will play there with thee;
134+
On my strand, lovely flowers their blossoms unfold,
135+
My mother shall grace thee with garments of gold."
136+
137+
"My father, my father, and dost thou not hear
138+
The words that the Erl-King now breathes in mine ear?"
139+
"Be calm, dearest child, 'tis thy fancy deceives;
140+
'Tis the sad wind that sighs through the withering leaves."
141+
142+
"Wilt go, then, dear infant, wilt go with me there?
143+
My daughters shall tend thee with sisterly care;
144+
My daughters by night their glad festival keep,
145+
They'll dance thee, and rock thee, and sing thee to sleep."
146+
147+
"My father, my father, and dost thou not see,
148+
How the Erl-King his daughters has brought here for me?"
149+
"My darling, my darling, I see it aright,
150+
'Tis the aged grey willows deceiving thy sight."
151+
152+
"I love thee, I'm charm'd by thy beauty, dear boy!
153+
And if thou'rt unwilling, then force I'll employ."
154+
"My father, my father, he seizes me fast,
155+
For sorely the Erl-King has hurt me at last."
156+
157+
The father now gallops, with terror half wild,
158+
He grasps in his arms the poor shuddering child;
159+
He reaches his courtyard with toil and with dread, –
160+
The child in his arms finds he motionless, dead.
161+
""")
162+
.padding()
163+
}
164+
}
31165
}

Example/cards/ExampleChildContentView.xib

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
3-
<device id="retina4_7" orientation="portrait">
4-
<adaptation id="fullscreen"/>
5-
</device>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
3+
<device id="retina4_7" orientation="portrait" appearance="light"/>
64
<dependencies>
75
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
97
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
108
</dependencies>
119
<objects>
@@ -16,7 +14,7 @@
1614
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1715
<subviews>
1816
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="miC-Fv-7h9">
19-
<rect key="frame" x="16" y="8" width="343" height="651"/>
17+
<rect key="frame" x="16" y="192" width="343" height="467"/>
2018
<string key="text">Who rides there so late through the night dark and drear?
2119
The father it is, with his infant so dear;
2220
He holdeth the boy tightly clasp'd in his arm,
@@ -60,16 +58,28 @@ The child in his arms finds he motionless, dead.</string>
6058
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
6159
<nil key="highlightedColor"/>
6260
</label>
61+
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="erlking" translatesAutoresizingMaskIntoConstraints="NO" id="eZG-SF-4iT">
62+
<rect key="frame" x="0.0" y="0.0" width="375" height="176"/>
63+
<constraints>
64+
<constraint firstAttribute="width" secondItem="eZG-SF-4iT" secondAttribute="height" multiplier="640:300" id="DD0-Za-H6m"/>
65+
</constraints>
66+
</imageView>
6367
</subviews>
6468
<color key="backgroundColor" red="0.13725490200000001" green="0.69411764710000001" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
6569
<constraints>
6670
<constraint firstItem="miC-Fv-7h9" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="IuK-Hf-Zgg"/>
71+
<constraint firstItem="miC-Fv-7h9" firstAttribute="top" secondItem="eZG-SF-4iT" secondAttribute="bottom" constant="16" id="Kez-VD-uti"/>
6772
<constraint firstAttribute="trailing" secondItem="miC-Fv-7h9" secondAttribute="trailing" constant="16" id="YMl-Xn-hwv"/>
6873
<constraint firstAttribute="bottom" secondItem="miC-Fv-7h9" secondAttribute="bottom" constant="8" id="b27-ev-qpJ"/>
69-
<constraint firstItem="miC-Fv-7h9" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="8" id="bO8-eg-BwL"/>
74+
<constraint firstItem="eZG-SF-4iT" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="hrq-An-fgl"/>
75+
<constraint firstAttribute="trailing" secondItem="eZG-SF-4iT" secondAttribute="trailing" id="sKn-qQ-A8j"/>
76+
<constraint firstItem="eZG-SF-4iT" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="uMS-ZZ-95l"/>
7077
</constraints>
7178
<nil key="simulatedStatusBarMetrics"/>
72-
<point key="canvasLocation" x="33.5" y="54.5"/>
79+
<point key="canvasLocation" x="53.600000000000001" y="49.025487256371818"/>
7380
</view>
7481
</objects>
82+
<resources>
83+
<image name="erlking" width="640" height="300"/>
84+
</resources>
7585
</document>

Example/cards/ExamplePageCard.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ import TGCardViewController
1414
class ExamplePageCard: TGPageCard {
1515

1616
init() {
17-
let card1 = ExampleCityCard(city: .sydney)
18-
let card2 = ExampleTableCard(pushOnTap: false)
19-
let card3 = ExampleChildCard()
20-
let card4 = ExampleCityCard(city: .nuremberg)
21-
super.init(cards: [card1, card2, card3, card4])
17+
var cards: [TGCard] = []
18+
cards.append(ExampleCityCard(city: .sydney))
19+
cards.append(ExampleTableCard(pushOnTap: false))
20+
if #available(iOS 16.0, *) {
21+
cards.append(ExampleChildCard())
22+
}
23+
cards.append(ExampleCityCard(city: .nuremberg))
24+
25+
super.init(cards: cards)
2226

2327
// Custom accessory for testing jumping around
2428
let jumpButton = UIButton(type: .roundedRect)

Example/cards/ExampleRootCard.swift

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,17 @@ fileprivate class DataSource : NSObject, UITableViewDelegate, UITableViewDataSou
121121

122122
var onSelect: ((Item) -> Void)?
123123

124-
let items: [Item] = [
125-
(title: "Show Erlking", card: ExampleChildCard()),
126-
(title: "Show Table", card: ExampleTableCard()),
127-
(title: "Show Pages", card: ExamplePageCard()),
128-
(title: "Custom title", card: ExampleCustomTitleCard())
129-
]
124+
let items: [Item] = {
125+
var items: [Item] = [
126+
(title: "Show Table", card: ExampleTableCard()),
127+
(title: "Show Pages", card: ExamplePageCard()),
128+
(title: "Custom title", card: ExampleCustomTitleCard()),
129+
]
130+
if #available(iOS 16.0, *) {
131+
items.append((title: "Show Erlking", card: ExampleChildCard()))
132+
}
133+
return items
134+
}()
130135

131136
func handleSelection(_ indexPath: IndexPath) {
132137
onSelect?(items[indexPath.row])

Example/cards/ExampleTableCard.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Copyright © 2017 SkedGo Pty Ltd. All rights reserved.
77
//
88

9+
import SwiftUI
910
import UIKit
1011

1112
import TGCardViewController
@@ -23,7 +24,12 @@ class ExampleTableCard : TGTableCard {
2324

2425
self.pushOnTap = pushOnTap
2526

26-
super.init(title: "London stops", dataSource: source, delegate: source, accessoryView: ExampleAccessoryView.instantiate(), mapManager: mapManager)
27+
super.init(
28+
title: .customExtended(TableTitle()),
29+
dataSource: source,
30+
delegate: source,
31+
mapManager: mapManager
32+
)
2733

2834
handleMacSelection = source.handleSelection
2935
bottomMapToolBarItems = [UIButton.dummyDetailDisclosureButton()]
@@ -34,6 +40,9 @@ class ExampleTableCard : TGTableCard {
3440

3541
tableView.dragInteractionEnabled = true
3642

43+
let topSpacer = UIView(frame: .init(x: 0, y: 0, width: 100, height: 100))
44+
tableView.tableHeaderView = topSpacer
45+
3746
if pushOnTap {
3847
source.onSelect = {
3948
let card = ExampleTableChildCard(annotation: $0)
@@ -48,3 +57,14 @@ class ExampleTableCard : TGTableCard {
4857
}
4958

5059
}
60+
61+
struct TableTitle: View {
62+
63+
var body: some View {
64+
HStack {
65+
Text("London Stops")
66+
.font(.largeTitle)
67+
}
68+
}
69+
70+
}

0 commit comments

Comments
 (0)