Adding number with puller instead of old textfield
This commit is contained in:
parent
512a50b217
commit
1d377b8faa
@ -84,6 +84,14 @@
|
||||
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */; };
|
||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
||||
7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7AC355492969652F00889457 /* SwiftEntryKit */; };
|
||||
7AC3554C29696A1C00889457 /* MainTabController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3554B29696A1C00889457 /* MainTabController.swift */; };
|
||||
7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3554D29696C4500889457 /* DummyNewController.swift */; };
|
||||
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3554F29696D5A00889457 /* NewNumberController.swift */; };
|
||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555129696E3F00889457 /* UIView+layout.swift */; };
|
||||
7AC35554296973E100889457 /* ACButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC35553296973E100889457 /* ACButton.swift */; };
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC355582969746600889457 /* UIControl.swift */; };
|
||||
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555A296995B200889457 /* UIEdgeInsets.swift */; };
|
||||
7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC76D7A270083AE0084DB27 /* TextView.swift */; };
|
||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
|
||||
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C94250D037700F237B2 /* ShowEventController.swift */; };
|
||||
@ -251,6 +259,13 @@
|
||||
7AB562B9249C9E9B00473D53 /* VehicleRegion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRegion.swift; sourceTree = "<group>"; };
|
||||
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
||||
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
|
||||
7AC3554B29696A1C00889457 /* MainTabController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabController.swift; sourceTree = "<group>"; };
|
||||
7AC3554D29696C4500889457 /* DummyNewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DummyNewController.swift; sourceTree = "<group>"; };
|
||||
7AC3554F29696D5A00889457 /* NewNumberController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewNumberController.swift; sourceTree = "<group>"; };
|
||||
7AC3555129696E3F00889457 /* UIView+layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+layout.swift"; sourceTree = "<group>"; };
|
||||
7AC35553296973E100889457 /* ACButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACButton.swift; sourceTree = "<group>"; };
|
||||
7AC355582969746600889457 /* UIControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = "<group>"; };
|
||||
7AC3555A296995B200889457 /* UIEdgeInsets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsets.swift; sourceTree = "<group>"; };
|
||||
7AC76D7A270083AE0084DB27 /* TextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextView.swift; sourceTree = "<group>"; };
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.swift; sourceTree = "<group>"; };
|
||||
7ADF6C94250D037700F237B2 /* ShowEventController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowEventController.swift; sourceTree = "<group>"; };
|
||||
@ -285,6 +300,7 @@
|
||||
7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */,
|
||||
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */,
|
||||
7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */,
|
||||
7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */,
|
||||
7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */,
|
||||
7A35177B27E23F8800DC538C /* Eureka in Frameworks */,
|
||||
7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */,
|
||||
@ -368,6 +384,7 @@
|
||||
7A1146FF23FDE7E500B424AF /* AutoCat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC355552969742800889457 /* ACUIKit */,
|
||||
7A530B7C24017FBE00CBFE6E /* Cells */,
|
||||
7A11471423FDEAF800B424AF /* Controllers */,
|
||||
7A3F07A924360D9100E59687 /* Extensions */,
|
||||
@ -408,6 +425,9 @@
|
||||
7A3F07AC2436350B00E59687 /* SearchController.swift */,
|
||||
7AEFE727240455E200910EB7 /* SettingsController.swift */,
|
||||
7A6F095D26DB9F85003A965D /* NotesController.swift */,
|
||||
7AC3554B29696A1C00889457 /* MainTabController.swift */,
|
||||
7AC3554D29696C4500889457 /* DummyNewController.swift */,
|
||||
7AC3554F29696D5A00889457 /* NewNumberController.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
@ -558,6 +578,33 @@
|
||||
path = Location;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AC355552969742800889457 /* ACUIKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC355562969743800889457 /* Views */,
|
||||
7AC355572969744100889457 /* Extensions */,
|
||||
);
|
||||
path = ACUIKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AC355562969743800889457 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC35553296973E100889457 /* ACButton.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AC355572969744100889457 /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7AC3555129696E3F00889457 /* UIView+layout.swift */,
|
||||
7AC355582969746600889457 /* UIControl.swift */,
|
||||
7AC3555A296995B200889457 /* UIEdgeInsets.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7AF6D1DE2677A7E00086EA64 /* AutoCatTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -642,6 +689,7 @@
|
||||
7A813DC02508C4D900CC93B9 /* ExceptionCatcher */,
|
||||
7AABDE1C2532F3EB0041AFC6 /* PKHUD */,
|
||||
7A35177A27E23F8800DC538C /* Eureka */,
|
||||
7AC355492969652F00889457 /* SwiftEntryKit */,
|
||||
);
|
||||
productName = AutoCat;
|
||||
productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */;
|
||||
@ -734,6 +782,7 @@
|
||||
7AABDE1B2532F3EB0041AFC6 /* XCRemoteSwiftPackageReference "PKHUD" */,
|
||||
7AABDE21253327F10041AFC6 /* XCRemoteSwiftPackageReference "DifferenceKit" */,
|
||||
7A35177927E23F8800DC538C /* XCRemoteSwiftPackageReference "Eureka" */,
|
||||
7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */,
|
||||
);
|
||||
productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -792,12 +841,14 @@
|
||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
||||
7A99406426E4BFAE002E9CB6 /* VehicleNoteCell.swift in Sources */,
|
||||
7A8AB76B25A1D95500ECF2C1 /* SourceStatusRow.swift in Sources */,
|
||||
7AC3554C29696A1C00889457 /* MainTabController.swift in Sources */,
|
||||
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */,
|
||||
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */,
|
||||
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */,
|
||||
7A0B96A0257D6D4B000B39AD /* MultilineLabelRow.swift in Sources */,
|
||||
7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */,
|
||||
7A761C0B267E8FF90005F28F /* Error.swift in Sources */,
|
||||
7AC3555029696D5A00889457 /* NewNumberController.swift in Sources */,
|
||||
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */,
|
||||
7A2DE69E2589606A00A113FC /* ImageGridRow.swift in Sources */,
|
||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */,
|
||||
@ -808,6 +859,7 @@
|
||||
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */,
|
||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */,
|
||||
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
|
||||
7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */,
|
||||
7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */,
|
||||
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */,
|
||||
7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */,
|
||||
@ -836,10 +888,14 @@
|
||||
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */,
|
||||
7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */,
|
||||
7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */,
|
||||
7AC3555229696E3F00889457 /* UIView+layout.swift in Sources */,
|
||||
7A0420AD2561A0B100034941 /* OsagoController.swift in Sources */,
|
||||
7AC355592969746600889457 /* UIControl.swift in Sources */,
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||
7A21112A24FC3D7E003BBF6F /* AudioEngine.swift in Sources */,
|
||||
7AC35554296973E100889457 /* ACButton.swift in Sources */,
|
||||
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */,
|
||||
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */,
|
||||
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */,
|
||||
7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */,
|
||||
7AE492A1259232F000322D2E /* MultilineLinkRow.swift in Sources */,
|
||||
@ -1080,7 +1136,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 100;
|
||||
CURRENT_PROJECT_VERSION = 101;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
@ -1105,7 +1161,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 100;
|
||||
CURRENT_PROJECT_VERSION = 101;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
@ -1329,6 +1385,14 @@
|
||||
minimumVersion = 1.1.5;
|
||||
};
|
||||
};
|
||||
7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/huri000/SwiftEntryKit";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/onevcat/Kingfisher";
|
||||
@ -1380,6 +1444,11 @@
|
||||
package = 7AABDE1B2532F3EB0041AFC6 /* XCRemoteSwiftPackageReference "PKHUD" */;
|
||||
productName = PKHUD;
|
||||
};
|
||||
7AC355492969652F00889457 /* SwiftEntryKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */;
|
||||
productName = SwiftEntryKit;
|
||||
};
|
||||
7AF58D332402A91C00CE01A0 /* Kingfisher */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
||||
|
||||
@ -89,6 +89,15 @@
|
||||
"revision" : "6190d0cefff3013e77ed567e6b074f324e5c5bf5",
|
||||
"version" : "6.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swiftentrykit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/huri000/SwiftEntryKit",
|
||||
"state" : {
|
||||
"revision" : "5ad36cccf0c4b9fea32f4e9b17a8e38f07563ef0",
|
||||
"version" : "2.0.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 2
|
||||
|
||||
@ -4,38 +4,18 @@
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
<Breakpoints>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "C14D0996-5708-44D2-A6BA-4A4B50B522EE"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "CF01B44D-372B-4C78-A197-7FDEC607CE0E"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
scope = "1"
|
||||
stopOnStyle = "0">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SymbolicBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "B15A9E9C-A0CD-4FC9-8E24-DD93FB1B677F"
|
||||
shouldBeEnabled = "No"
|
||||
uuid = "676638C8-1CC5-4C04-98B0-1C0D6CB28B76"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
symbolName = "UITableViewAlertForLayoutOutsideViewHierarchy"
|
||||
moduleName = "">
|
||||
<Locations>
|
||||
<Location
|
||||
uuid = "B15A9E9C-A0CD-4FC9-8E24-DD93FB1B677F - 620169ab4c7c265a"
|
||||
uuid = "676638C8-1CC5-4C04-98B0-1C0D6CB28B76 - 620169ab4c7c265a"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
@ -47,74 +27,5 @@
|
||||
</Locations>
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "CFC2DD73-A257-45F3-A7A8-7D9462944F86"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "AutoCat/Extensions/AudioEngine.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "12"
|
||||
endingLineNumber = "12"
|
||||
landmarkName = "setCategoryAsync(_:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "4F72AC4F-FBCE-4C10-8BC2-2F434652DB31"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "42580582-DA14-40D4-869A-FF91FCA9957C"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
breakpointStackSelectionBehavior = "1"
|
||||
scope = "1"
|
||||
stopOnStyle = "0">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "CA002588-A70B-4F96-BD13-7CC6C39BC2D5"
|
||||
shouldBeEnabled = "Yes"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "../../../Library/Developer/Xcode/DerivedData/AutoCat-fhilwnlnsrpirleiajogdcyhyyey/SourcePackages/checkouts/RxSwift/RxCocoa/Foundation/URLSession+Rx.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "193"
|
||||
endingLineNumber = "193"
|
||||
landmarkName = "data(request:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
<BreakpointProxy
|
||||
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
|
||||
<BreakpointContent
|
||||
uuid = "2B5BCCAE-FCA2-43D1-9835-08DB7E24B91C"
|
||||
shouldBeEnabled = "No"
|
||||
ignoreCount = "0"
|
||||
continueAfterRunningActions = "No"
|
||||
filePath = "AutoCat/Utils/RxRealmDataSource.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "35"
|
||||
endingLineNumber = "35"
|
||||
landmarkName = "init(table:data:cellIdentifier:onSizeChanged:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
</BreakpointProxy>
|
||||
</Breakpoints>
|
||||
</Bucket>
|
||||
|
||||
35
AutoCat/ACUIKit/Extensions/UIControl.swift
Normal file
35
AutoCat/ACUIKit/Extensions/UIControl.swift
Normal file
@ -0,0 +1,35 @@
|
||||
import UIKit
|
||||
|
||||
extension UIControl {
|
||||
|
||||
func addAction(for controlEvent: UIControl.Event = .touchUpInside, _ closure: @escaping () -> Void) {
|
||||
|
||||
addActionImpl(for: controlEvent, closure: closure)
|
||||
}
|
||||
|
||||
func addActionAsync(for controlEvent: UIControl.Event = .touchUpInside, _ closure: @escaping () async -> Void) {
|
||||
|
||||
addActionImpl(for: controlEvent) {
|
||||
Task {
|
||||
await closure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addActionImpl(for controlEvents: UIControl.Event, closure: @escaping () -> Void) {
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
addAction(UIAction { _ in closure() }, for: controlEvents)
|
||||
} else {
|
||||
@objc class ClosureSleeve: NSObject {
|
||||
let closure:()->()
|
||||
init(_ closure: @escaping()->()) { self.closure = closure }
|
||||
@objc func invoke() { closure() }
|
||||
}
|
||||
let sleeve = ClosureSleeve(closure)
|
||||
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
|
||||
objc_setAssociatedObject(self, "\(UUID())", sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
15
AutoCat/ACUIKit/Extensions/UIEdgeInsets.swift
Normal file
15
AutoCat/ACUIKit/Extensions/UIEdgeInsets.swift
Normal file
@ -0,0 +1,15 @@
|
||||
import UIKit
|
||||
|
||||
extension UIEdgeInsets {
|
||||
|
||||
init(all: CGFloat) {
|
||||
self.init(top: all, left: all, bottom: all, right: all)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIEdgeInsets {
|
||||
|
||||
static func all(_ value: CGFloat) -> UIEdgeInsets {
|
||||
.init(all: value)
|
||||
}
|
||||
}
|
||||
13
AutoCat/ACUIKit/Extensions/UIView+layout.swift
Normal file
13
AutoCat/ACUIKit/Extensions/UIView+layout.swift
Normal file
@ -0,0 +1,13 @@
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
|
||||
func pin(to view: UIView, insets: UIEdgeInsets = .zero) {
|
||||
NSLayoutConstraint.activate([
|
||||
leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left),
|
||||
trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right),
|
||||
topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top),
|
||||
bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom)
|
||||
])
|
||||
}
|
||||
}
|
||||
80
AutoCat/ACUIKit/Views/ACButton.swift
Normal file
80
AutoCat/ACUIKit/Views/ACButton.swift
Normal file
@ -0,0 +1,80 @@
|
||||
import UIKit
|
||||
|
||||
enum ACButtonStyle {
|
||||
case generic
|
||||
case roundedBlue
|
||||
}
|
||||
|
||||
class ACButton: UIButton {
|
||||
|
||||
private var style: ACButtonStyle = .generic
|
||||
|
||||
convenience init(style: ACButtonStyle = .roundedBlue, title: String, onTap: @escaping () -> Void) {
|
||||
self.init()
|
||||
self.style(style)
|
||||
self.onTap(onTap)
|
||||
self.title(title)
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
convenience init(style: ACButtonStyle = .roundedBlue, title: String, onTapAsync: @escaping () async -> Void) {
|
||||
self.init()
|
||||
self.style(style)
|
||||
self.onTapAsync(onTapAsync)
|
||||
self.title(title)
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
if style == .roundedBlue {
|
||||
self.layer.opacity = self.isEnabled ? 1 : 0.5
|
||||
} else {
|
||||
self.layer.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func style(_ style: ACButtonStyle) -> ACButton {
|
||||
|
||||
self.style = style
|
||||
|
||||
switch style {
|
||||
case .generic:
|
||||
break
|
||||
case .roundedBlue:
|
||||
backgroundColor = .systemBlue
|
||||
layer.cornerRadius = 6
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func title(_ title: String) -> ACButton {
|
||||
|
||||
setTitle(title, for: .normal)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func enable(_ enabled: Bool) -> ACButton {
|
||||
isEnabled = enabled
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func onTap(_ handler: @escaping () -> Void) -> ACButton {
|
||||
|
||||
addAction(for: .touchUpInside, handler)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func onTapAsync(_ handler: @escaping () async -> Void) -> ACButton {
|
||||
|
||||
addActionAsync(for: .touchUpInside, handler)
|
||||
return self
|
||||
}
|
||||
}
|
||||
@ -173,17 +173,17 @@
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="EventCell" id="QIb-Hv-tvk" customClass="EventCell" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="430.5"/>
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="431.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QIb-Hv-tvk" id="Ypt-ch-fGT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="430.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="431.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HP8-oO-yhP">
|
||||
<rect key="frame" x="16" y="8" width="343" height="414.5"/>
|
||||
<rect key="frame" x="16" y="8" width="343" height="415.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="k4Z-KM-byE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="335" height="414.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="335" height="415.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcQ-Wz-gJ0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="335" height="201"/>
|
||||
@ -192,7 +192,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1tQ-zM-6T9">
|
||||
<rect key="frame" x="0.0" y="209" width="335" height="205.5"/>
|
||||
<rect key="frame" x="0.0" y="209" width="335" height="206.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@ -200,7 +200,7 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="750" verticalHuggingPriority="251" image="person" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="CFI-xa-eLs">
|
||||
<rect key="frame" x="343" y="1.5" width="0.0" height="412"/>
|
||||
<rect key="frame" x="343" y="1.5" width="0.0" height="413"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
@ -597,50 +597,19 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="Peq-Zq-kNT">
|
||||
<rect key="frame" x="62.5" y="68" width="250" height="114.5"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="A001AA 777" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8FU-Gy-4MU" customClass="SwiftMaskTextfield" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="250" height="50.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldDidBeginEditing:" destination="twc-qR-t1G" eventType="editingDidBegin" id="Kvu-xK-QuZ"/>
|
||||
<action selector="textFieldDidChange:" destination="twc-qR-t1G" eventType="editingChanged" id="x69-Ym-Zdm"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ync-fd-xQI" customClass="CustomButton" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="66.5" width="250" height="48"/>
|
||||
<color key="backgroundColor" systemColor="systemBlueColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="48" id="YE0-lL-OwL"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<state key="normal" title="Check">
|
||||
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="checkTapped:" destination="twc-qR-t1G" eventType="touchUpInside" id="WiK-lL-7q7"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="250" id="lqO-Yy-NyQ"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="JKr-UE-x8f">
|
||||
<rect key="frame" x="0.0" y="206.5" width="375" height="411.5"/>
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="623"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="3ON-lr-UlV" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="84.5"/>
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="94.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3ON-lr-UlV" id="IGw-UK-ebp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="84.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="94.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="6IZ-gM-D28">
|
||||
<rect key="frame" x="8" y="8" width="359" height="68.5"/>
|
||||
<rect key="frame" x="8" y="8" width="359" height="78.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="Rz6-wH-O1q">
|
||||
<rect key="frame" x="0.0" y="0.0" width="359" height="20"/>
|
||||
@ -675,18 +644,15 @@
|
||||
</subviews>
|
||||
</stackView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="bottom" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="nX1-KA-jqp">
|
||||
<rect key="frame" x="0.0" y="28" width="359" height="40.5"/>
|
||||
<rect key="frame" x="0.0" y="28" width="359" height="50.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VHX-o0-3BP" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.5" width="295" height="40"/>
|
||||
<rect key="frame" x="0.0" y="0.5" width="295" height="50"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<color key="tintColor" systemColor="systemOrangeColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="vzo-oF-mWb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="bottom" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="ApN-fV-Qw4">
|
||||
<rect key="frame" x="303" y="0.5" width="56" height="40"/>
|
||||
<rect key="frame" x="303" y="10.5" width="56" height="40"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="1.01.2021" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rKr-3e-WYb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="56" height="16"/>
|
||||
@ -731,11 +697,9 @@
|
||||
<viewLayoutGuide key="safeArea" id="3Jk-FW-b3r"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="top" secondItem="3Jk-FW-b3r" secondAttribute="top" id="PUg-nV-TN6"/>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="trailing" secondItem="3Jk-FW-b3r" secondAttribute="trailing" id="Tc5-ri-5RD"/>
|
||||
<constraint firstItem="Peq-Zq-kNT" firstAttribute="top" secondItem="3Jk-FW-b3r" secondAttribute="top" constant="24" id="Tft-8o-Cb1"/>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="bottom" secondItem="3Jk-FW-b3r" secondAttribute="bottom" id="Zwj-XD-UhD"/>
|
||||
<constraint firstItem="Peq-Zq-kNT" firstAttribute="centerX" secondItem="g5m-iL-O7A" secondAttribute="centerX" id="fXl-Na-1gb"/>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="top" secondItem="Peq-Zq-kNT" secondAttribute="bottom" constant="24" id="mm9-rL-KQ1"/>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="bottom" secondItem="g5m-iL-O7A" secondAttribute="bottom" id="Zwj-XD-UhD"/>
|
||||
<constraint firstItem="JKr-UE-x8f" firstAttribute="leading" secondItem="3Jk-FW-b3r" secondAttribute="leading" id="vfN-fu-5pA"/>
|
||||
</constraints>
|
||||
</view>
|
||||
@ -754,9 +718,7 @@
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="check" destination="Ync-fd-xQI" id="oxC-p6-Mou"/>
|
||||
<outlet property="history" destination="JKr-UE-x8f" id="GP9-Gz-WBm"/>
|
||||
<outlet property="number" destination="8FU-Gy-4MU" id="PwP-Pn-eeO"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="h7Y-mR-2Ti" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
@ -812,10 +774,10 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1704.8" y="-701.19940029985014"/>
|
||||
</scene>
|
||||
<!--Tab Bar Controller-->
|
||||
<!--Main Tab Controller-->
|
||||
<scene sceneID="YhQ-kn-py3">
|
||||
<objects>
|
||||
<tabBarController id="s9R-9a-TOT" sceneMemberID="viewController">
|
||||
<tabBarController id="s9R-9a-TOT" customClass="MainTabController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="7gI-Jq-j4q">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="49"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@ -824,6 +786,7 @@
|
||||
<connections>
|
||||
<segue destination="TSb-ZG-qfD" kind="relationship" relationship="viewControllers" id="Bwf-98-gjF"/>
|
||||
<segue destination="RK6-pn-2Bg" kind="relationship" relationship="viewControllers" id="KNz-WF-Kyy"/>
|
||||
<segue destination="dxo-XS-x8Q" kind="relationship" relationship="viewControllers" id="MgV-gx-h7p"/>
|
||||
<segue destination="GCa-Re-j14" kind="relationship" relationship="viewControllers" id="FGp-f6-fUh"/>
|
||||
<segue destination="4jU-Z3-PF2" kind="relationship" relationship="viewControllers" id="aH2-IT-86l"/>
|
||||
</connections>
|
||||
@ -951,6 +914,22 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="199" y="143"/>
|
||||
</scene>
|
||||
<!--Add-->
|
||||
<scene sceneID="bCG-fh-cdJ">
|
||||
<objects>
|
||||
<viewController id="dxo-XS-x8Q" customClass="DummyNewController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="7Rw-QC-Av7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<viewLayoutGuide key="safeArea" id="vIj-pz-YHR"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Add" image="plus" catalog="system" id="X0w-PF-Dn8"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="C2T-ZN-q8G" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="929" y="-260"/>
|
||||
</scene>
|
||||
<!--Global Events Controller-->
|
||||
<scene sceneID="akP-Pw-M4Q">
|
||||
<objects>
|
||||
@ -989,11 +968,11 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="7518" y="144"/>
|
||||
</scene>
|
||||
<!--Check-->
|
||||
<!--History-->
|
||||
<scene sceneID="pUX-kf-oY1">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="TSb-ZG-qfD" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="Check" image="check" landscapeImage="check-compact" id="QJd-35-4OB"/>
|
||||
<tabBarItem key="tabBarItem" title="History" image="clock.arrow.circlepath" catalog="system" id="QJd-35-4OB"/>
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="AAc-4d-GNh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
@ -1084,14 +1063,14 @@
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="check" width="25" height="25"/>
|
||||
<image name="check-compact" width="18" height="18"/>
|
||||
<image name="clock.arrow.circlepath" catalog="system" width="128" height="112"/>
|
||||
<image name="doc.on.doc" catalog="system" width="116" height="128"/>
|
||||
<image name="exclamationmark.arrow.triangle.2.circlepath" catalog="system" width="128" height="104"/>
|
||||
<image name="line.horizontal.3.decrease" catalog="system" width="128" height="73"/>
|
||||
<image name="map" catalog="system" width="128" height="112"/>
|
||||
<image name="person" catalog="system" width="128" height="121"/>
|
||||
<image name="play.fill" catalog="system" width="117" height="128"/>
|
||||
<image name="plus" catalog="system" width="128" height="113"/>
|
||||
<image name="record" width="31" height="31"/>
|
||||
<image name="record-compact" width="23" height="23"/>
|
||||
<image name="search" width="23" height="23"/>
|
||||
|
||||
@ -23,6 +23,8 @@ class VehicleCell: UITableViewCell, ConfigurableCell {
|
||||
func configure(with vehicle: Vehicle) {
|
||||
self.name.text = vehicle.brand?.name?.original ?? "<unknown>"
|
||||
self.plate.number = PlateNumber(vehicle.getNumber())
|
||||
self.plate.fontSize = 40
|
||||
|
||||
if vehicle.unrecognized {
|
||||
self.plate.foreground = .systemRed
|
||||
} else if vehicle.outdated {
|
||||
|
||||
@ -27,29 +27,30 @@ extension String.StringInterpolation {
|
||||
}
|
||||
}
|
||||
|
||||
class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegate, PNKeyboardDelegate {
|
||||
class CheckController: UIViewController, UITableViewDelegate, UISearchResultsUpdating {
|
||||
|
||||
@IBOutlet weak var number: SwiftMaskTextfield!
|
||||
@IBOutlet weak var check: UIButton!
|
||||
@IBOutlet weak var history: UITableView!
|
||||
|
||||
private let bag = DisposeBag()
|
||||
private var historyDataSource: RealmSectionedDataSource<Vehicle, VehicleCell>!
|
||||
private var historyFilter: HistoryFilter = .all
|
||||
|
||||
private let searchController = UISearchController(searchResultsController: nil)
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
searchController.searchResultsUpdater = self
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.searchBar.placeholder = NSLocalizedString("Search plate numbers", comment: "")
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
navigationItem.searchController = searchController
|
||||
|
||||
guard let realm = try? Realm() else { return }
|
||||
|
||||
self.hideKeyboardWhenTappedAround()
|
||||
let keyboard = PNKeyboard(target: self.number)
|
||||
keyboard.delegate = self
|
||||
//self.number.formatPattern = "@###@@###"
|
||||
self.number.inputView = keyboard
|
||||
self.check.isEnabled = false
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.historyDataSource = RealmSectionedDataSource(table: self.history, data: realm.objects(Vehicle.self).sorted(byKeyPath: "updatedDate", ascending: false)) { count in
|
||||
@ -61,11 +62,6 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
||||
NotificationCenter.default.addObserver(self, selector:#selector(self.calendarDayDidChange(_:)), name:NSNotification.Name.NSCalendarDayChanged, object:nil)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
//self.navigationController?.setNavigationBarHidden(true, animated: false)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
if let index = self.history.indexPathForSelectedRow {
|
||||
@ -86,7 +82,9 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
||||
switch ad.quickAction {
|
||||
case .check:
|
||||
ad.quickAction = .none
|
||||
self.number.becomeFirstResponder()
|
||||
if let tabBar = tabBarController as? MainTabController {
|
||||
tabBar.showCheckPuller()
|
||||
}
|
||||
break
|
||||
case .checkNumber(let number, let event):
|
||||
ad.quickAction = .none
|
||||
@ -204,13 +202,8 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
||||
|
||||
// MARK: - Checking new number
|
||||
|
||||
@IBAction func checkTapped(_ sender: UIButton) {
|
||||
guard let number = self.number.text else { return }
|
||||
|
||||
func checkTapped(number: String) {
|
||||
let numberNormalized = number.filter { !$0.isWhitespace }.uppercased()
|
||||
self.number.resignFirstResponder()
|
||||
self.number.text = nil
|
||||
self.check.isEnabled = false
|
||||
|
||||
var events: [VehicleEvent] = []
|
||||
do {
|
||||
@ -258,29 +251,6 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITextFieldDelegate
|
||||
|
||||
@IBAction func textFieldDidBeginEditing(_ textField: UITextField) {
|
||||
RxLocationManager.requestCurrentLocation().subscribe().disposed(by: self.bag)
|
||||
}
|
||||
|
||||
@IBAction func textFieldDidChange(_ textField: UITextField) {
|
||||
guard let text = textField.text else {
|
||||
self.check.isEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
self.check.isEnabled = text.count >= 8
|
||||
}
|
||||
|
||||
// MARK: - PNKeyboardDelegate
|
||||
|
||||
func returnClicked() {
|
||||
if self.check.isEnabled {
|
||||
self.checkTapped(self.check)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
@ -322,6 +292,23 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UISearchResultsUpdating
|
||||
|
||||
func updateSearchResults(for searchController: UISearchController) {
|
||||
guard let realm = try? Realm() else {
|
||||
return
|
||||
}
|
||||
|
||||
var query = realm.objects(Vehicle.self)
|
||||
.sorted(byKeyPath: "updatedDate", ascending: false)
|
||||
|
||||
if let text = searchController.searchBar.text?.uppercased(), !text.isEmpty {
|
||||
query = query.filter("number CONTAINS '\(text)'")
|
||||
}
|
||||
|
||||
historyDataSource.setQuery(query)
|
||||
}
|
||||
|
||||
// MARK: - Contextual actions
|
||||
|
||||
func update(vehicle: Vehicle) {
|
||||
|
||||
6
AutoCat/Controllers/DummyNewController.swift
Normal file
6
AutoCat/Controllers/DummyNewController.swift
Normal file
@ -0,0 +1,6 @@
|
||||
import UIKit
|
||||
|
||||
/// Dummy controller for detecting tap on "plus" tab
|
||||
class DummyNewController: UIViewController {
|
||||
|
||||
}
|
||||
55
AutoCat/Controllers/MainTabController.swift
Normal file
55
AutoCat/Controllers/MainTabController.swift
Normal file
@ -0,0 +1,55 @@
|
||||
import UIKit
|
||||
import SwiftEntryKit
|
||||
import AutoCatCore
|
||||
import RxSwift
|
||||
|
||||
class MainTabController: UITabBarController, UITabBarControllerDelegate {
|
||||
|
||||
private let bag = DisposeBag()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
self.delegate = self
|
||||
}
|
||||
|
||||
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
|
||||
if viewController is DummyNewController {
|
||||
showCheckPuller()
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func showCheckPuller() {
|
||||
guard let checkNav = viewControllers?.first as? UINavigationController,
|
||||
let checkController = checkNav.viewControllers.first as? CheckController
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
var attributes = EKAttributes.bottomToast
|
||||
attributes.displayDuration = .infinity
|
||||
attributes.entryBackground = .color(color: .init(.secondarySystemBackground))
|
||||
attributes.screenBackground = .color(color: EKColor(UIColor(white: 0, alpha: 0.7)))
|
||||
attributes.roundCorners = .top(radius: 24)
|
||||
attributes.screenInteraction = .dismiss
|
||||
attributes.entryInteraction = .forward
|
||||
attributes.entranceAnimation = .init(translate: .init(duration: 0.2))
|
||||
attributes.exitAnimation = .init(translate: .init(duration: 0.2))
|
||||
|
||||
let newNumberController = NewNumberController()
|
||||
newNumberController.onCheck = { number in
|
||||
SwiftEntryKit.dismiss {
|
||||
self.selectedIndex = 0
|
||||
checkController.checkTapped(number: number)
|
||||
}
|
||||
}
|
||||
|
||||
SwiftEntryKit.display(entry: newNumberController, using: attributes)
|
||||
|
||||
// User probably just saw a vehicle and is about to start entering plate number
|
||||
// Requesting current location ASAP while we still close to initial location
|
||||
RxLocationManager.requestCurrentLocation().subscribe().disposed(by: self.bag)
|
||||
}
|
||||
}
|
||||
83
AutoCat/Controllers/NewNumberController.swift
Normal file
83
AutoCat/Controllers/NewNumberController.swift
Normal file
@ -0,0 +1,83 @@
|
||||
import UIKit
|
||||
import AutoCatCore
|
||||
|
||||
class NewNumberController: UIViewController {
|
||||
|
||||
public var onCheck: ((String) -> Void)?
|
||||
|
||||
private lazy var keyboardView: PNKeyboard = {
|
||||
let keyboard = PNKeyboard(target: self.plateView)
|
||||
keyboard.delegate = self
|
||||
return keyboard
|
||||
}()
|
||||
|
||||
private lazy var plateView: PlateView = {
|
||||
let view = PlateView(frame: .zero)
|
||||
view.fontSize = 48
|
||||
view.number = PlateNumber("")
|
||||
view.onChange = onNumberChanged
|
||||
view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
view.accessibilityIdentifier = "plateView"
|
||||
return view
|
||||
}()
|
||||
|
||||
private lazy var checkButton: ACButton = {
|
||||
let button = ACButton(title: NSLocalizedString("Check", comment: ""), onTap: check)
|
||||
button.isEnabled = false
|
||||
button.contentEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 8)
|
||||
button.accessibilityIdentifier = "checkButton"
|
||||
return button
|
||||
}()
|
||||
|
||||
private lazy var stackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [plateView, checkButton])
|
||||
stack.axis = .horizontal
|
||||
stack.spacing = 16
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
return stack
|
||||
}()
|
||||
|
||||
private let titleLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = NSLocalizedString("Check number", comment: "")
|
||||
label.font = UIFont.preferredFont(forTextStyle: .headline)
|
||||
label.textAlignment = .center
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var mainStackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [titleLabel, stackView, keyboardView])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 16
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
return stack
|
||||
}()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.addSubview(mainStackView)
|
||||
mainStackView.pin(to: view, insets: .init(top: 16, left: 16, bottom: 16, right: 16))
|
||||
}
|
||||
|
||||
func check() {
|
||||
guard let number = plateView.number, number.isValid else {
|
||||
return
|
||||
}
|
||||
|
||||
let numberNormalized = number.asString().filter { !$0.isWhitespace }.uppercased()
|
||||
onCheck?(numberNormalized)
|
||||
}
|
||||
|
||||
func onNumberChanged() {
|
||||
checkButton.isEnabled = plateView.number?.isValid ?? false
|
||||
}
|
||||
}
|
||||
|
||||
extension NewNumberController: PNKeyboardDelegate {
|
||||
|
||||
func returnClicked() {
|
||||
check()
|
||||
}
|
||||
}
|
||||
@ -15,10 +15,12 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource
|
||||
|
||||
var vehicle: Vehicle? {
|
||||
didSet {
|
||||
loadViewIfNeeded()
|
||||
self.updateReport()
|
||||
self.form.allSections.forEach { $0.reload() }
|
||||
self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false)
|
||||
//loadViewIfNeeded()
|
||||
if isViewLoaded {
|
||||
self.updateReport()
|
||||
self.form.allSections.forEach { $0.reload() }
|
||||
self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,8 +207,8 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource
|
||||
return value ? yes : no
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
guard let ad = UIApplication.shared.delegate as? AppDelegate else { return }
|
||||
|
||||
self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: animated)
|
||||
|
||||
@ -33,6 +33,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe
|
||||
searchController.searchResultsUpdater = self
|
||||
searchController.obscuresBackgroundDuringPresentation = false
|
||||
searchController.searchBar.placeholder = NSLocalizedString("Search plate numbers", comment: "")
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
navigationItem.searchController = searchController
|
||||
definesPresentationContext = true
|
||||
|
||||
|
||||
@ -18,16 +18,23 @@ class RealmSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource
|
||||
private var cellIdentifier: String
|
||||
private var filterPredicate: FilterPredicate<Item>?
|
||||
|
||||
private let onSizeChanged: ((Int) -> Void)?
|
||||
|
||||
init(table: UITableView, data: Results<Item>, cellIdentifier: String = String(describing: Cell.self), onSizeChanged: ((Int) -> Void)? = nil) {
|
||||
self.tv = table
|
||||
self.data = data
|
||||
self.cellIdentifier = cellIdentifier
|
||||
self.onSizeChanged = onSizeChanged
|
||||
super.init()
|
||||
self.tv.dataSource = self
|
||||
self.tv.reloadData()
|
||||
|
||||
startObservingChanges()
|
||||
}
|
||||
|
||||
func startObservingChanges() {
|
||||
self.notificationToken = self.data.observe { changes in
|
||||
onSizeChanged?(self.data.count)
|
||||
self.onSizeChanged?(self.data.count)
|
||||
switch changes {
|
||||
case .initial:
|
||||
self.reload(animated: false)
|
||||
@ -170,4 +177,9 @@ class RealmSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func setQuery(_ query: Results<Item>) {
|
||||
self.data = query
|
||||
startObservingChanges()
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,11 +66,9 @@ class RxSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where I
|
||||
|
||||
DispatchQueue.global().async {
|
||||
let newSections = self.items.groupedByDate(type: self.sortParam)
|
||||
let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||
DispatchQueue.main.async {
|
||||
self.tv.reload(using: changeset, with: .automatic) { newSects in
|
||||
self.sections = newSects
|
||||
}
|
||||
self.sections = newSections
|
||||
self.tv.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,8 +110,11 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate {
|
||||
private weak var target: UIKeyInput?
|
||||
weak var delegate: PNKeyboardDelegate?
|
||||
|
||||
init(target: UIKeyInput) {
|
||||
private let insets: UIEdgeInsets
|
||||
|
||||
init(target: UIKeyInput, insets: UIEdgeInsets = .zero) {
|
||||
self.target = target
|
||||
self.insets = insets
|
||||
super.init(frame: .zero)
|
||||
self.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
@ -176,10 +179,10 @@ class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate {
|
||||
self.addSubview(mainStack)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16),
|
||||
mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16),
|
||||
mainStack.topAnchor.constraint(equalTo: self.topAnchor, constant: 16),
|
||||
mainStack.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -16)
|
||||
mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: insets.left),
|
||||
mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -insets.right),
|
||||
mainStack.topAnchor.constraint(equalTo: self.topAnchor, constant: insets.top),
|
||||
mainStack.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -insets.bottom)
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import UIKit
|
||||
import AutoCatCore
|
||||
|
||||
private class CALayerAnimationsDisablingDelegate: NSObject, CALayerDelegate {
|
||||
static let shared = CALayerAnimationsDisablingDelegate()
|
||||
private let null = NSNull()
|
||||
|
||||
func action(for layer: CALayer, forKey event: String) -> CAAction? {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
class PlateView: UIView {
|
||||
// Some driver plate parameters from "ГОСТ Р 50577-93"
|
||||
// http://docs.cntd.ru/document/gost-r-50577-93
|
||||
@ -26,10 +35,26 @@ class PlateView: UIView {
|
||||
self.layoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
var fontSize: CGFloat = 10 {
|
||||
didSet {
|
||||
self.layoutSubviews()
|
||||
}
|
||||
}
|
||||
|
||||
var onChange: (() -> Void)?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setup()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
func setup() {
|
||||
self.layer.backgroundColor = UIColor.clear.cgColor
|
||||
|
||||
self.bgLayer.cornerRadius = 6 //self.bounds.height/8
|
||||
@ -56,7 +81,21 @@ class PlateView: UIView {
|
||||
|
||||
self.flagLayer.contentsScale = UIScreen.main.scale
|
||||
self.layer.addSublayer(self.flagLayer)
|
||||
|
||||
disableAnimations(for: [
|
||||
bgLayer,
|
||||
mainBgLayer,
|
||||
regionBgLayer,
|
||||
numberLayer,
|
||||
regionLayer,
|
||||
countryLayer,
|
||||
flagLayer
|
||||
])
|
||||
}
|
||||
|
||||
func disableAnimations(for layers: [CALayer]) {
|
||||
layers.forEach { $0.delegate = CALayerAnimationsDisablingDelegate.shared }
|
||||
}
|
||||
|
||||
private func pathForFlag(in rect: CGRect) -> CGPath {
|
||||
let path = CGMutablePath()
|
||||
@ -116,15 +155,31 @@ class PlateView: UIView {
|
||||
}
|
||||
|
||||
override var intrinsicContentSize: CGSize {
|
||||
guard self.bounds.width != 0 || self.bounds.height != 0 else {
|
||||
return CGSize.zero
|
||||
}
|
||||
|
||||
let curAspectRatio = self.bounds.height/self.bounds.width
|
||||
if curAspectRatio >= PlateView.aspectRatio {
|
||||
return CGSize(width: self.bounds.width, height: self.bounds.width*PlateView.aspectRatio)
|
||||
} else {
|
||||
return CGSize(width: self.bounds.height/PlateView.aspectRatio, height: self.bounds.height)
|
||||
}
|
||||
|
||||
let height = fontSize/1.1 + 4
|
||||
let width = height/PlateView.aspectRatio
|
||||
print("")
|
||||
return CGSize(width: width, height: height)
|
||||
}
|
||||
}
|
||||
|
||||
extension PlateView: UIKeyInput {
|
||||
|
||||
var hasText: Bool {
|
||||
number?.hasText ?? false
|
||||
}
|
||||
|
||||
func insertText(_ text: String) {
|
||||
if number?.insertText(text) == true {
|
||||
layoutSubviews()
|
||||
onChange?()
|
||||
}
|
||||
}
|
||||
|
||||
func deleteBackward() {
|
||||
if number?.deleteBackward() == true {
|
||||
layoutSubviews()
|
||||
onChange?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,6 +70,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Check date" = "Дата проверки";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Check number" = "Проверить номер";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Check parameters" = "Параметры проверки";
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
/* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "7QQ-Qi-qEw"; */
|
||||
"7QQ-Qi-qEw.title" = "Назад";
|
||||
|
||||
/* Class = "UITextField"; placeholder = "A001AA 777"; ObjectID = "8FU-Gy-4MU"; */
|
||||
"8FU-Gy-4MU.placeholder" = "A001AA 777";
|
||||
|
||||
/* Class = "UILabel"; text = "00:00"; ObjectID = "bFQ-eU-5YJ"; */
|
||||
"bFQ-eU-5YJ.text" = "00:00";
|
||||
|
||||
@ -31,8 +28,8 @@
|
||||
/* Class = "UITabBarItem"; title = "Records"; ObjectID = "lxF-EY-z8V"; */
|
||||
"lxF-EY-z8V.title" = "Аудио";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Check"; ObjectID = "QJd-35-4OB"; */
|
||||
"QJd-35-4OB.title" = "Проверка";
|
||||
/* Class = "UITabBarItem"; title = "History"; ObjectID = "QJd-35-4OB"; */
|
||||
"QJd-35-4OB.title" = "История";
|
||||
|
||||
/* Class = "UILabel"; text = "or"; ObjectID = "SXb-1Q-TxY"; */
|
||||
"SXb-1Q-TxY.text" = "или";
|
||||
@ -43,8 +40,8 @@
|
||||
/* Class = "UITextField"; placeholder = "Email"; ObjectID = "Uey-88-eZL"; */
|
||||
"Uey-88-eZL.placeholder" = "Email";
|
||||
|
||||
/* Class = "UIButton"; normalTitle = "Check"; ObjectID = "Ync-fd-xQI"; */
|
||||
"Ync-fd-xQI.normalTitle" = "Проверить";
|
||||
/* Class = "UITabBarItem"; title = "Add"; ObjectID = "X0w-PF-Dn8"; */
|
||||
"X0w-PF-Dn8.title" = "Добавить";
|
||||
|
||||
/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "zEL-ph-E2f"; */
|
||||
"zEL-ph-E2f.title" = "Настройки";
|
||||
|
||||
@ -5,12 +5,16 @@ import DifferenceKit
|
||||
public struct DateSection<T>: Differentiable, DifferentiableSection, Hashable where T: Differentiable & Hashable {
|
||||
|
||||
var timestamp: Double = 0
|
||||
public var header: String
|
||||
var dateString: String
|
||||
public var elements: [T]
|
||||
|
||||
public var header: String {
|
||||
"\(dateString) (\(elements.count))"
|
||||
}
|
||||
|
||||
public init<C>(source: DateSection<T>, elements: C) where C : Swift.Collection, C.Element == Self.Collection.Element {
|
||||
self.timestamp = source.timestamp
|
||||
self.header = source.header
|
||||
self.dateString = source.dateString
|
||||
self.elements = Array(elements)
|
||||
}
|
||||
|
||||
@ -31,20 +35,20 @@ public struct DateSection<T>: Differentiable, DifferentiableSection, Hashable wh
|
||||
let date = Date(timeIntervalSince1970: timestamp)
|
||||
let dateInRegion = DateInRegion(date, region: Region.current)
|
||||
if dateInRegion.isToday {
|
||||
self.header = NSLocalizedString("Today", comment: "")
|
||||
self.dateString = NSLocalizedString("Today", comment: "")
|
||||
}
|
||||
else if dateInRegion.isYesterday {
|
||||
self.header = NSLocalizedString("Yesterday", comment: "")
|
||||
self.dateString = NSLocalizedString("Yesterday", comment: "")
|
||||
} else if dateInRegion.isAfterDate(weekStart, granularity: .day) {
|
||||
formatter.dateFormat = "EEEE"
|
||||
self.header = formatter.string(from: date)
|
||||
self.dateString = formatter.string(from: date)
|
||||
} else if dateInRegion.isAfterDate(monthStart, orEqual: false, granularity: .day) {
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
self.header = formatter.string(from: date)
|
||||
self.dateString = formatter.string(from: date)
|
||||
} else {
|
||||
formatter.dateFormat = "LLLL yyyy"
|
||||
self.header = formatter.string(from: date)
|
||||
self.dateString = formatter.string(from: date)
|
||||
}
|
||||
|
||||
self.timestamp = timestamp
|
||||
@ -74,7 +78,7 @@ public struct DateSection<T>: Differentiable, DifferentiableSection, Hashable wh
|
||||
// MARK: - Differentiable
|
||||
|
||||
public var differenceIdentifier: String {
|
||||
return header
|
||||
return dateString
|
||||
}
|
||||
|
||||
public func isContentEqual(to source: DateSection<T>) -> Bool {
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
import Foundation
|
||||
|
||||
public class PlateNumber {
|
||||
private var number: String
|
||||
private var numberEnglish: String
|
||||
private var number: String = ""
|
||||
private var numberEnglish: String = ""
|
||||
|
||||
private let maxLength = 9
|
||||
private let mainPartLength = 6
|
||||
private let isDigitMask = [false, true, true, true, false, false, true, true, true]
|
||||
|
||||
public init(_ string: String) {
|
||||
self.number = string
|
||||
self.numberEnglish = String(self.number.map { Constants.pnLettersMap[$0] ?? $0 })
|
||||
setNumber(string)
|
||||
}
|
||||
|
||||
public func asString() -> String {
|
||||
@ -14,12 +17,56 @@ public class PlateNumber {
|
||||
}
|
||||
|
||||
public func mainPart() -> String {
|
||||
let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
|
||||
return String(self.numberEnglish[..<index])
|
||||
return String(self.numberEnglish.prefix(mainPartLength))
|
||||
}
|
||||
|
||||
public func region() -> String {
|
||||
let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
|
||||
return String(self.numberEnglish[index...])
|
||||
guard numberEnglish.count > mainPartLength else {
|
||||
return ""
|
||||
}
|
||||
|
||||
return String(self.numberEnglish.dropFirst(mainPartLength))
|
||||
}
|
||||
|
||||
public var isValid: Bool {
|
||||
number.count >= 8
|
||||
}
|
||||
|
||||
public func setNumber(_ number: String) {
|
||||
self.number = number
|
||||
self.numberEnglish = String(self.number.map { Constants.pnLettersMap[$0] ?? $0 })
|
||||
}
|
||||
|
||||
// MARK: - UIKeyInput
|
||||
|
||||
public var hasText: Bool {
|
||||
return !number.isEmpty
|
||||
}
|
||||
|
||||
// Returns true if number was changed
|
||||
public func insertText(_ text: String) -> Bool {
|
||||
guard number.count < maxLength, text.count == 1 else {
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: Do proper validation
|
||||
// if text.first?.isNumber == isDigitMask[number.count] {
|
||||
// setNumber(number + text)
|
||||
// return true
|
||||
// } else {
|
||||
// return false
|
||||
// }
|
||||
|
||||
setNumber(number + text)
|
||||
return true
|
||||
}
|
||||
|
||||
public func deleteBackward() -> Bool {
|
||||
guard !number.isEmpty else {
|
||||
return false
|
||||
}
|
||||
|
||||
setNumber(String(number.dropLast()))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +364,7 @@ public class Vehicle: Object, Decodable, Identifiable, Differentiable, Cloneable
|
||||
// MARK: - Exportable
|
||||
|
||||
public static var csvHeader: String {
|
||||
return "Model, Color, Year, Plate Number, VIN, STS, PTS, Added Date, Updated date, Locations"
|
||||
return "Plate Number, Model, Color, Year, Owners, VIN, STS, PTS, Added Date, Updated date, Locations, Notes"
|
||||
}
|
||||
|
||||
public var csvLine: String {
|
||||
@ -379,16 +379,22 @@ public class Vehicle: Object, Decodable, Identifiable, Differentiable, Cloneable
|
||||
eventsString.append(location + "; " + date + "\r\n")
|
||||
}
|
||||
|
||||
return String(format: "\"%@\", %@, %d, %@, %@, %@, %@, \"%@\", \"%@\", \"%@\"",
|
||||
let notesString = self.notes.reduce("") { partialResult, note in
|
||||
partialResult + note.text + "\r\n"
|
||||
}
|
||||
|
||||
return String(format: "%@, \"%@\", %@, %d, %d, %@, %@, %@, \"%@\", \"%@\", \"%@\", \"%@\"",
|
||||
self.number,
|
||||
model,
|
||||
self.color ?? "",
|
||||
self.year,
|
||||
self.number,
|
||||
self.ownershipPeriods.count,
|
||||
self.vin1 ?? "",
|
||||
self.sts ?? "",
|
||||
self.pts ?? "",
|
||||
added,
|
||||
updated,
|
||||
eventsString)
|
||||
eventsString,
|
||||
notesString)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user