Skip to content

Commit f9cf131

Browse files
Merge pull request #66 from 3sidedcube/release/v1.6.0
Release/v1.6.0
2 parents 96593d3 + 657b811 commit f9cf131

12 files changed

+324
-56
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ DerivedData
3131
# Carthage/Checkouts
3232

3333
Carthage/Build
34+
ThunderTable.framework.zip

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
language: swift
22
xcode_project: ThunderTable.xcodeproj # path to your xcodeproj folder
3-
osx_image: xcode11
3+
osx_image: xcode12
44
env:
55
global:
66
- LC_CTYPE=en_US.UTF-8
77
- LANG=en_US.UTF-8
88
matrix:
99
include:
1010
- xcode_scheme: ThunderTable
11-
xcode_destination: platform=iOS Simulator,OS=13.0,name=iPhone 11 Pro Max
11+
xcode_destination: platform=iOS Simulator,OS=14.0,name=iPhone 11 Pro Max

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class MyTableViewController: TableViewController {
5959
}
6060
```
6161

62+
# Building Binaries for Carthage
63+
64+
Since Xcode 12 there has been issues with building Carthage binaries caused by the inclusion of a secondary arm64 slice in the generated binary needed for Apple Silicon on macOS. This means that rather than simply using `carthage build --archive` you need to use the `./carthage-build build --archive` command which uses the script included with this repo. For more information, see the issue on Carthage's github [here](https://github.com/Carthage/Carthage/issues/3019)
65+
66+
We will be investigating moving over to use SPM as an agency soon, and will also look into migrating to use .xcframeworks as soon as Carthage have support for it.
67+
6268
# Code level documentation
6369
Documentation is available for the entire library in AppleDoc format. This is available in the framework itself or in the [Hosted Version](http://3sidedcube.github.io/iOS-ThunderTable/)
6470

ThunderTable.xcodeproj/project.pbxproj

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
B114F1122035CC19005D52F2 /* InputPickerViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B114F1102035CC19005D52F2 /* InputPickerViewCell.xib */; };
1818
B11CB8CF1F320DA600E58116 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11CB8CE1F320DA600E58116 /* Extensions.swift */; };
1919
B11CEB9F20498F0C001308B3 /* CNContact+Row.swift in Sources */ = {isa = PBXBuildFile; fileRef = B11CEB79204977A2001308B3 /* CNContact+Row.swift */; };
20+
B12D31092512385700274462 /* InputInlineDatePickerViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B12D31072512385700274462 /* InputInlineDatePickerViewCell.xib */; };
21+
B12D310A2512385700274462 /* InputInlineDatePickerViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12D31082512385700274462 /* InputInlineDatePickerViewCell.swift */; };
2022
B14F945120444E820014F694 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F945020444E820014F694 /* AppDelegate.swift */; };
2123
B14F945320444E820014F694 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F945220444E820014F694 /* ViewController.swift */; };
2224
B14F945620444E820014F694 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14F945420444E820014F694 /* Main.storyboard */; };
@@ -103,6 +105,8 @@
103105
B114F1102035CC19005D52F2 /* InputPickerViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = InputPickerViewCell.xib; sourceTree = "<group>"; };
104106
B11CB8CE1F320DA600E58116 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
105107
B11CEB79204977A2001308B3 /* CNContact+Row.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CNContact+Row.swift"; sourceTree = "<group>"; };
108+
B12D31072512385700274462 /* InputInlineDatePickerViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = InputInlineDatePickerViewCell.xib; sourceTree = "<group>"; };
109+
B12D31082512385700274462 /* InputInlineDatePickerViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputInlineDatePickerViewCell.swift; sourceTree = "<group>"; };
106110
B14F944E20444E810014F694 /* ThunderTableDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThunderTableDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
107111
B14F945020444E820014F694 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
108112
B14F945220444E820014F694 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@@ -225,6 +229,13 @@
225229
path = Cells;
226230
sourceTree = "<group>";
227231
};
232+
B12D31062512347800274462 /* Extensions */ = {
233+
isa = PBXGroup;
234+
children = (
235+
);
236+
name = Extensions;
237+
sourceTree = "<group>";
238+
};
228239
B14F944F20444E810014F694 /* ThunderTableDemo */ = {
229240
isa = PBXGroup;
230241
children = (
@@ -264,6 +275,7 @@
264275
B17BAA341D89639100844421 /* ThunderTable */ = {
265276
isa = PBXGroup;
266277
children = (
278+
B12D31062512347800274462 /* Extensions */,
267279
B1B5C6D324FFCA8C00F05CE8 /* Scroll Offset Caching */,
268280
B1D10CD01DA540CF003FBCB4 /* Theme.swift */,
269281
B19C53261D8B036600B30A35 /* ApplicationLoadingIndicatorManager.swift */,
@@ -306,6 +318,8 @@
306318
B19C53211D8B02F600B30A35 /* Cells */ = {
307319
isa = PBXGroup;
308320
children = (
321+
B12D31082512385700274462 /* InputInlineDatePickerViewCell.swift */,
322+
B12D31072512385700274462 /* InputInlineDatePickerViewCell.xib */,
309323
B1C2C56D1FFCF20F00D968C5 /* InputDatePickerViewCell.swift */,
310324
B1C2C56E1FFCF20F00D968C5 /* InputDatePickerViewCell.xib */,
311325
B114F10F2035CC19005D52F2 /* InputPickerViewCell.swift */,
@@ -500,6 +514,7 @@
500514
B108319E1F068FDF008B565D /* TableImageViewCell.xib in Resources */,
501515
B1784D871D8C3A8A007358EA /* InputSliderViewCell.xib in Resources */,
502516
B1EC80FF1FDE85EA00C8EE72 /* DefaultTableViewCell.xib in Resources */,
517+
B12D31092512385700274462 /* InputInlineDatePickerViewCell.xib in Resources */,
503518
B1C2C5701FFCF20F00D968C5 /* InputDatePickerViewCell.xib in Resources */,
504519
B1A8983E2047117D0028CAE4 /* Value2TableViewCell.xib in Resources */,
505520
B1AD7EC11D8BFC9D00BFCA34 /* InputTextViewCell.xib in Resources */,
@@ -540,6 +555,7 @@
540555
B17BAA581D89643800844421 /* TableSection.swift in Sources */,
541556
B1784D831D8C3A60007358EA /* InputSliderRow.swift in Sources */,
542557
B1C2C56C1FFCEE3100D968C5 /* InputDatePickerRow.swift in Sources */,
558+
B12D310A2512385700274462 /* InputInlineDatePickerViewCell.swift in Sources */,
543559
B1EC81021FDE86BF00C8EE72 /* SubtitleTableViewCell.swift in Sources */,
544560
B1B5C6D724FFCAAD00F05CE8 /* ScrollOffsetManagable.swift in Sources */,
545561
B1B679611D89807A00B66FD8 /* TableViewCell.swift in Sources */,
@@ -623,7 +639,7 @@
623639
INFOPLIST_FILE = ThunderTableDemo/Info.plist;
624640
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
625641
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
626-
MARKETING_VERSION = 1.5.0;
642+
MARKETING_VERSION = 1.6.0;
627643
PRODUCT_BUNDLE_IDENTIFIER = com.3sidedcube.ThunderTableDemo;
628644
PRODUCT_NAME = "$(TARGET_NAME)";
629645
SWIFT_VERSION = 5.0;
@@ -646,7 +662,7 @@
646662
INFOPLIST_FILE = ThunderTableDemo/Info.plist;
647663
IPHONEOS_DEPLOYMENT_TARGET = 11.2;
648664
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
649-
MARKETING_VERSION = 1.5.0;
665+
MARKETING_VERSION = 1.6.0;
650666
PRODUCT_BUNDLE_IDENTIFIER = com.3sidedcube.ThunderTableDemo;
651667
PRODUCT_NAME = "$(TARGET_NAME)";
652668
SWIFT_VERSION = 5.0;
@@ -786,7 +802,7 @@
786802
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
787803
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
788804
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
789-
MARKETING_VERSION = 1.5.0;
805+
MARKETING_VERSION = 1.6.0;
790806
PRODUCT_BUNDLE_IDENTIFIER = com.threesidedcube.ThunderTable;
791807
PRODUCT_NAME = "$(TARGET_NAME)";
792808
SKIP_INSTALL = YES;
@@ -811,7 +827,7 @@
811827
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
812828
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
813829
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
814-
MARKETING_VERSION = 1.5.0;
830+
MARKETING_VERSION = 1.6.0;
815831
PRODUCT_BUNDLE_IDENTIFIER = com.threesidedcube.ThunderTable;
816832
PRODUCT_NAME = "$(TARGET_NAME)";
817833
SKIP_INSTALL = YES;

ThunderTable/InputDatePickerRow.swift

Lines changed: 104 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,80 @@ import UIKit
1111
/// A row which displays a date picker in the keyboard for the user
1212
/// to select a date and formats the date nicely
1313
open class InputDatePickerRow: InputTableRow {
14+
15+
/// An direct map enum to `UIDatePickerStyle` so we can provide the `preferredDatePickerStyle` on iOS
16+
/// versions before iOS 13.4 without getting "Stored properties cannot be marked potentially unavailable with '@available'"
17+
/// compiler warning!
18+
public enum PickerStyle: Int {
19+
/// Automatically pick the best style available for the current platform & mode.
20+
case automatic = 0
21+
22+
/// Use the wheels (UIPickerView) style. Editing occurs inline.
23+
case wheels = 1
24+
25+
/// Use a compact style for the date picker. Editing occurs in an overlay.
26+
case compact = 2
27+
28+
/// Use a style for the date picker that allows editing in place.
29+
case inline = 3
30+
31+
@available (iOS 13.4, *)
32+
var datePickerStyle: UIDatePickerStyle {
33+
switch self {
34+
case .automatic:
35+
return .automatic
36+
case .compact:
37+
return .compact
38+
case .inline:
39+
if #available(iOS 14.0, *) {
40+
return .inline
41+
} else {
42+
return .automatic
43+
}
44+
case .wheels:
45+
return .wheels
46+
}
47+
}
48+
49+
/// Returns the correct cell class for the style given the mode the date picker is set to
50+
/// - Parameter mode: The date picker mode the date picker is in
51+
/// - Returns: A table view cell class used to render the cell
52+
func cellClass(for mode: UIDatePicker.Mode) -> UITableViewCell.Type {
53+
54+
// Even old versions of iOS can support `inline` no matter how much of a hot mess it is visually!
55+
if self == .inline {
56+
// Return new class
57+
return InputInlineDatePickerViewCell.self
58+
}
59+
60+
// New `inline` and `compact` date pickers are only available on iOS 14
61+
guard #available(iOS 14, *) else {
62+
return InputDatePickerViewCell.self
63+
}
64+
65+
switch mode {
66+
case .countDownTimer:
67+
// countdown timer doesn't have an `inline` or `compact` representation, so we
68+
// keep the traditional `wheels` cell for this case
69+
return InputDatePickerViewCell.self
70+
default:
71+
switch self {
72+
// Automatic, Compact and Inline should all use the inline style cell
73+
case .automatic, .compact, .inline:
74+
return InputInlineDatePickerViewCell.self
75+
// All others (.wheels) should use the original date picker cell
76+
default:
77+
return InputDatePickerViewCell.self
78+
}
79+
}
80+
}
81+
}
1482

1583
/// The date picker mode for the row
1684
open var mode: UIDatePicker.Mode = .dateAndTime
85+
86+
/// The preferred date picker style for the row, this will only have an effect on iOS > 13.4
87+
open var preferredDatePickerStyle: PickerStyle = .automatic
1788

1889
/// The minimum date allowed by the row
1990
open var minimumDate: Date?
@@ -60,32 +131,51 @@ open class InputDatePickerRow: InputTableRow {
60131
}
61132

62133
open override var cellClass: UITableViewCell.Type? {
63-
return InputDatePickerViewCell.self
134+
return preferredDatePickerStyle.cellClass(for: mode)
64135
}
65136

66137
open override func configure(cell: UITableViewCell, at indexPath: IndexPath, in tableViewController: TableViewController) {
67138

68-
guard let datePickerCell = cell as? InputDatePickerViewCell else { return }
139+
guard let datePickerCell = cell as? InputDatePickerViewCell else { return }
69140

70141
super.configure(cell: cell, at: indexPath, in: tableViewController)
71142

72143
// Targets and selectors
73-
updateTargetsAndSelectors(for: datePickerCell.textField)
144+
// If we have a text field we use that for targets and selectors
145+
if let textField = datePickerCell.inputTextField {
146+
updateTargetsAndSelectors(for: textField)
147+
// Otherwise we add them to the date picker to make sure we still get callbacks!
148+
} else if let datePicker = datePickerCell.datePicker {
149+
updateTargetsAndSelectors(for: datePicker)
150+
}
151+
74152
datePickerCell.dateFormatter = dateFormatter
75-
datePickerCell.textField.delegate = self
76-
datePickerCell.datePicker.addTarget(self, action: #selector(handleChange(sender:)), for: .valueChanged)
77-
datePickerCell.datePicker.addTarget(datePickerCell, action: #selector(InputDatePickerViewCell.updateLabel(sender:)), for: .valueChanged)
153+
datePickerCell.inputTextField?.delegate = self
154+
datePickerCell.datePicker?.addTarget(self, action: #selector(handleChange(sender:)), for: .valueChanged)
155+
156+
datePickerCell.datePicker?.addTarget(datePickerCell, action: #selector(datePickerCell.updateInputTextFieldText(sender:)), for: .valueChanged)
78157

79158
// Setting up date picker
80-
datePickerCell.datePicker.minimumDate = minimumDate
81-
datePickerCell.datePicker.maximumDate = maximumDate
82-
datePickerCell.datePicker.datePickerMode = mode
159+
datePickerCell.datePicker?.minimumDate = minimumDate
160+
datePickerCell.datePicker?.maximumDate = maximumDate
161+
datePickerCell.datePicker?.datePickerMode = mode
162+
163+
if #available(iOS 13.4, *) {
164+
datePickerCell.datePicker?.preferredDatePickerStyle = preferredDatePickerStyle.datePickerStyle
165+
}
83166

84-
if let dateValue = value as? Date {
85-
datePickerCell.textField.text = dateFormatter.string(from: dateValue)
86-
} else {
87-
datePickerCell.textField.text = nil
88-
}
167+
if let textField = datePickerCell.inputTextField {
168+
169+
if let dateValue = value as? Date {
170+
textField.text = dateFormatter.string(from: dateValue)
171+
} else {
172+
textField.text = nil
173+
}
174+
175+
} else {
176+
177+
datePickerCell.datePicker?.date = value as? Date ?? Date()
178+
}
89179
}
90180

91181
@objc private func handleChange(sender: UIDatePicker) {

ThunderTable/InputDatePickerViewCell.swift

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,31 @@
88

99
import UIKit
1010

11+
/// A `TableViewCell` subclass with an image, title label, and a text field aligned horizontally
12+
///
13+
/// This cell subclass allows the user to pick a date using a `UIDatePicker`set as the text field's
14+
/// `inputView`, meaning it shows in-place of the default iOS keyboard
1115
open class InputDatePickerViewCell: TableViewCell {
12-
13-
@IBOutlet weak public var textField: UITextField!
14-
15-
public var datePicker = UIDatePicker()
16-
17-
internal var dateFormatter: DateFormatter = DateFormatter()
16+
17+
public var inputTextField: UITextField? {
18+
return textField
19+
}
20+
21+
/// The text field allowing the user to enter a date
22+
@IBOutlet weak public var textField: UITextField?
23+
24+
/// The date picker the user uses to pick the date
25+
@IBOutlet public var datePicker: UIDatePicker? = UIDatePicker()
26+
27+
/// The date formatter used to format the date displayed in `textField`
28+
public var dateFormatter: DateFormatter? = DateFormatter()
1829

1930
override open func becomeFirstResponder() -> Bool {
20-
return textField.becomeFirstResponder()
31+
return textField?.becomeFirstResponder() ?? false
2132
}
2233

2334
override open func resignFirstResponder() -> Bool {
24-
return textField.resignFirstResponder()
35+
return textField?.resignFirstResponder() ?? false
2536
}
2637

2738
open override func awakeFromNib() {
@@ -38,15 +49,15 @@ open class InputDatePickerViewCell: TableViewCell {
3849
UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDone(sender:)))
3950
]
4051

41-
textField.inputView = datePicker
42-
textField.inputAccessoryView = doneToolbar
52+
textField?.inputView = datePicker
53+
textField?.inputAccessoryView = doneToolbar
4354
}
4455

4556
@objc private func handleDone(sender: UIBarButtonItem) {
46-
textField.resignFirstResponder()
47-
}
48-
49-
@objc func updateLabel(sender: UIDatePicker) {
50-
textField.text = dateFormatter.string(from: sender.date)
57+
textField?.resignFirstResponder()
5158
}
59+
60+
@objc public func updateInputTextFieldText(sender: UIDatePicker) {
61+
textField?.text = dateFormatter?.string(from: sender.date)
62+
}
5263
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// InputInlineDatePickerViewCell.swift
3+
// ThunderTable
4+
//
5+
// Created by Simon Mitchell on 16/09/2020.
6+
// Copyright © 2020 3SidedCube. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
/// An `InputDatePickerViewCell` subclass with an image, title label, and a date picker aligned horizontally
12+
///
13+
/// This cell subclass allows the user to pick a date using a `UIDatePicker` embedded "inline"
14+
/// as one of the cell's subviews. This allows for use of
15+
open class InputInlineDatePickerViewCell: InputDatePickerViewCell {
16+
17+
override open func becomeFirstResponder() -> Bool {
18+
return datePicker?.becomeFirstResponder() ?? false
19+
}
20+
21+
override open func resignFirstResponder() -> Bool {
22+
return datePicker?.resignFirstResponder() ?? false
23+
}
24+
}

0 commit comments

Comments
 (0)