Skip to content

Commit

Permalink
New feature to extend a card's content under the title (#20)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nighthawk authored Apr 3, 2024
1 parent 7b1350d commit 6c1ee69
Show file tree
Hide file tree
Showing 21 changed files with 492 additions and 90 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ on:

jobs:
build_xcode:
runs-on: macos-11
runs-on: macos-14

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

examples:
runs-on: macos-11
runs-on: macos-14

steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '13.0' # latest-stable
xcode-version: 15.3 # latest-stable
- uses: actions/checkout@v2
- name: Build Example
run: |
cd Example
xcodebuild build -scheme 'Example' -destination 'platform=iOS Simulator,name=iPhone 13'
xcodebuild build -scheme 'Example' -destination 'platform=iOS Simulator,name=iPhone 14'
2 changes: 1 addition & 1 deletion Example/Example.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "erlking.jpg",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
"idiom" : "universal"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}
}
148 changes: 141 additions & 7 deletions Example/cards/ExampleChildCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,160 @@
// Copyright © 2017 SkedGo Pty Ltd. All rights reserved.
//

import SwiftUI
import UIKit
import MapKit

import TGCardViewController

@available(iOS 16.0, *)
class ExampleChildCard : TGPlainCard {

private let titleHandler: TitleHandler
var contentHost: UIHostingController<ContentView>

init() {
let content = ExampleChildContentView.instantiate()
let handler = TitleHandler()
self.titleHandler = handler

let contentHost = UIHostingController(rootView: ContentView())
self.contentHost = contentHost

let accessoryLabel = UILabel()
accessoryLabel.text = "This is an accessory view"
accessoryLabel.textColor = .cyan
accessoryLabel.textAlignment = .center
accessoryLabel.sizeToFit()
super.init(
title: .customExtended(TitleView(handler: handler)),
contentView: contentHost.view,
mapManager: TGMapManager.sydney
)

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

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

override func shouldToggleSeparator(show: Bool, offset: CGFloat) -> Bool {
titleHandler.scrollOffset = offset
return false
}

override func willAdjustContentAlpha(_ value: CGFloat) {
titleHandler.contentAlpha = value
}

}

private class TitleHandler: ObservableObject {
var onClose: () -> Void = {}

@Published var scrollOffset: CGFloat = 0
@Published var contentAlpha: CGFloat = 1

init() {}
}

@available(iOS 16.0, *)
struct TitleView: View {
@ObservedObject fileprivate var handler: TitleHandler

var body: some View {
HStack {
VStack(alignment: .leading) {
Text("A Child View")
.font(.largeTitle)
Text("With a subtitle")
.font(.headline)
}

Spacer()

Button {
handler.onClose()
} label: {
Image(systemName: "xmark.circle.fill")
}
}
.padding(.top, -10)
.padding(.horizontal)
.padding(.bottom)
.foregroundColor(handler.contentAlpha < 1 ? .black : Color.init(white: 1.0 - fabs(handler.scrollOffset) / 20))
// .foregroundStyle(.white)
.background {
Rectangle()
// .foregroundStyle(.clear)
// .overlay(
// Gradient(stops: [
// .init(color: .black.opacity(0.7), location: 0),
// .init(color: .black.opacity(0), location: 0.3),
// ])
// )
.foregroundStyle(.background)
.opacity(handler.contentAlpha < 1 ? 1 : fabs(handler.scrollOffset) / 20)
.offset(y: -20)
.padding(.bottom, -16)
}
}
}

@available(iOS 16.0, *)
struct ContentView: View {
var body: some View {
VStack {
Image("erlking")
.resizable()
.scaledToFit()
.overlay(
Gradient(stops: [
.init(color: .black.opacity(0.7), location: 0),
.init(color: .black.opacity(0), location: 0.3),
.init(color: .black.opacity(0), location: 0.8),
.init(color: .black.opacity(0.7), location: 1),
])
)

Text("""
Who rides there so late through the night dark and drear?
The father it is, with his infant so dear;
He holdeth the boy tightly clasp'd in his arm,
He holdeth him safely, he keepeth him warm.
"My son, wherefore seek'st thou thy face thus to hide?"
"Look, father, the Erl-King is close by our side!
Dost see not the Erl-King, with crown and with train?"
"My son, 'tis the mist rising over the plain."
"Oh, come, thou dear infant! oh come thou with me!
For many a game I will play there with thee;
On my strand, lovely flowers their blossoms unfold,
My mother shall grace thee with garments of gold."
"My father, my father, and dost thou not hear
The words that the Erl-King now breathes in mine ear?"
"Be calm, dearest child, 'tis thy fancy deceives;
'Tis the sad wind that sighs through the withering leaves."
"Wilt go, then, dear infant, wilt go with me there?
My daughters shall tend thee with sisterly care;
My daughters by night their glad festival keep,
They'll dance thee, and rock thee, and sing thee to sleep."
"My father, my father, and dost thou not see,
How the Erl-King his daughters has brought here for me?"
"My darling, my darling, I see it aright,
'Tis the aged grey willows deceiving thy sight."
"I love thee, I'm charm'd by thy beauty, dear boy!
And if thou'rt unwilling, then force I'll employ."
"My father, my father, he seizes me fast,
For sorely the Erl-King has hurt me at last."
The father now gallops, with terror half wild,
He grasps in his arms the poor shuddering child;
He reaches his courtyard with toil and with dread, –
The child in his arms finds he motionless, dead.
""")
.padding()
}
}
}
26 changes: 18 additions & 8 deletions Example/cards/ExampleChildContentView.xib
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<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">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand All @@ -16,7 +14,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<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">
<rect key="frame" x="16" y="8" width="343" height="651"/>
<rect key="frame" x="16" y="192" width="343" height="467"/>
<string key="text">Who rides there so late through the night dark and drear?
The father it is, with his infant so dear;
He holdeth the boy tightly clasp'd in his arm,
Expand Down Expand Up @@ -60,16 +58,28 @@ The child in his arms finds he motionless, dead.</string>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" image="erlking" translatesAutoresizingMaskIntoConstraints="NO" id="eZG-SF-4iT">
<rect key="frame" x="0.0" y="0.0" width="375" height="176"/>
<constraints>
<constraint firstAttribute="width" secondItem="eZG-SF-4iT" secondAttribute="height" multiplier="640:300" id="DD0-Za-H6m"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" red="0.13725490200000001" green="0.69411764710000001" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="miC-Fv-7h9" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="16" id="IuK-Hf-Zgg"/>
<constraint firstItem="miC-Fv-7h9" firstAttribute="top" secondItem="eZG-SF-4iT" secondAttribute="bottom" constant="16" id="Kez-VD-uti"/>
<constraint firstAttribute="trailing" secondItem="miC-Fv-7h9" secondAttribute="trailing" constant="16" id="YMl-Xn-hwv"/>
<constraint firstAttribute="bottom" secondItem="miC-Fv-7h9" secondAttribute="bottom" constant="8" id="b27-ev-qpJ"/>
<constraint firstItem="miC-Fv-7h9" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="8" id="bO8-eg-BwL"/>
<constraint firstItem="eZG-SF-4iT" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="hrq-An-fgl"/>
<constraint firstAttribute="trailing" secondItem="eZG-SF-4iT" secondAttribute="trailing" id="sKn-qQ-A8j"/>
<constraint firstItem="eZG-SF-4iT" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="uMS-ZZ-95l"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<point key="canvasLocation" x="33.5" y="54.5"/>
<point key="canvasLocation" x="53.600000000000001" y="49.025487256371818"/>
</view>
</objects>
<resources>
<image name="erlking" width="640" height="300"/>
</resources>
</document>
14 changes: 9 additions & 5 deletions Example/cards/ExamplePageCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import TGCardViewController
class ExamplePageCard: TGPageCard {

init() {
let card1 = ExampleCityCard(city: .sydney)
let card2 = ExampleTableCard(pushOnTap: false)
let card3 = ExampleChildCard()
let card4 = ExampleCityCard(city: .nuremberg)
super.init(cards: [card1, card2, card3, card4])
var cards: [TGCard] = []
cards.append(ExampleCityCard(city: .sydney))
cards.append(ExampleTableCard(pushOnTap: false))
if #available(iOS 16.0, *) {
cards.append(ExampleChildCard())
}
cards.append(ExampleCityCard(city: .nuremberg))

super.init(cards: cards)

// Custom accessory for testing jumping around
let jumpButton = UIButton(type: .roundedRect)
Expand Down
17 changes: 11 additions & 6 deletions Example/cards/ExampleRootCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,17 @@ fileprivate class DataSource : NSObject, UITableViewDelegate, UITableViewDataSou

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

let items: [Item] = [
(title: "Show Erlking", card: ExampleChildCard()),
(title: "Show Table", card: ExampleTableCard()),
(title: "Show Pages", card: ExamplePageCard()),
(title: "Custom title", card: ExampleCustomTitleCard())
]
let items: [Item] = {
var items: [Item] = [
(title: "Show Table", card: ExampleTableCard()),
(title: "Show Pages", card: ExamplePageCard()),
(title: "Custom title", card: ExampleCustomTitleCard()),
]
if #available(iOS 16.0, *) {
items.append((title: "Show Erlking", card: ExampleChildCard()))
}
return items
}()

func handleSelection(_ indexPath: IndexPath) {
onSelect?(items[indexPath.row])
Expand Down
22 changes: 21 additions & 1 deletion Example/cards/ExampleTableCard.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2017 SkedGo Pty Ltd. All rights reserved.
//

import SwiftUI
import UIKit

import TGCardViewController
Expand All @@ -23,7 +24,12 @@ class ExampleTableCard : TGTableCard {

self.pushOnTap = pushOnTap

super.init(title: "London stops", dataSource: source, delegate: source, accessoryView: ExampleAccessoryView.instantiate(), mapManager: mapManager)
super.init(
title: .customExtended(TableTitle()),
dataSource: source,
delegate: source,
mapManager: mapManager
)

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

tableView.dragInteractionEnabled = true

let topSpacer = UIView(frame: .init(x: 0, y: 0, width: 100, height: 100))
tableView.tableHeaderView = topSpacer

if pushOnTap {
source.onSelect = {
let card = ExampleTableChildCard(annotation: $0)
Expand All @@ -48,3 +57,14 @@ class ExampleTableCard : TGTableCard {
}

}

struct TableTitle: View {

var body: some View {
HStack {
Text("London Stops")
.font(.largeTitle)
}
}

}
Loading

0 comments on commit 6c1ee69

Please sign in to comment.