New custom keyboard for entering plate numbers

This commit is contained in:
Selim Mustafaev 2020-09-15 12:21:13 +03:00
parent 9faa57a4eb
commit f6d13af17e
17 changed files with 651 additions and 73 deletions

View File

@ -49,7 +49,6 @@
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3924A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift */; }; 7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3924A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift */; };
7A488C3E24A74B990054D0B2 /* Reactive+RxRealmDataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3A24A74B990054D0B2 /* Reactive+RxRealmDataSources.swift */; }; 7A488C3E24A74B990054D0B2 /* Reactive+RxRealmDataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3A24A74B990054D0B2 /* Reactive+RxRealmDataSources.swift */; };
7A488C3F24A74B990054D0B2 /* RealmBindObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3B24A74B990054D0B2 /* RealmBindObserver.swift */; }; 7A488C3F24A74B990054D0B2 /* RealmBindObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A488C3B24A74B990054D0B2 /* RealmBindObserver.swift */; };
7A530B78240010D900CBFE6E /* InputMask in Frameworks */ = {isa = PBXBuildFile; productRef = 7A530B77240010D900CBFE6E /* InputMask */; };
7A530B7A24001D3300CBFE6E /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7924001D3300CBFE6E /* CheckController.swift */; }; 7A530B7A24001D3300CBFE6E /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7924001D3300CBFE6E /* CheckController.swift */; };
7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */; }; 7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */; };
7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7F2401803A00CBFE6E /* Vehicle.swift */; }; 7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7F2401803A00CBFE6E /* Vehicle.swift */; };
@ -94,6 +93,9 @@
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; }; 7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; }; 7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C94250D037700F237B2 /* ShowEventController.swift */; }; 7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C94250D037700F237B2 /* ShowEventController.swift */; };
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */; };
7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */; };
7ADF6C9D250FA96000F237B2 /* SwiftMaskTextfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C9C250FA96000F237B2 /* SwiftMaskTextfield.swift */; };
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; }; 7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; }; 7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; }; 7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
@ -183,6 +185,9 @@
7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; }; 7AB67E8D2435D1A000258F61 /* CustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomButton.swift; sourceTree = "<group>"; };
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.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>"; }; 7ADF6C94250D037700F237B2 /* ShowEventController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowEventController.swift; sourceTree = "<group>"; };
7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PNKeyboard.swift; sourceTree = "<group>"; };
7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
7ADF6C9C250FA96000F237B2 /* SwiftMaskTextfield.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMaskTextfield.swift; sourceTree = "<group>"; };
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; }; 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; }; 7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; };
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; }; 7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
@ -209,7 +214,6 @@
7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */, 7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */,
7A11471F23FEA18700B424AF /* RxRelay in Frameworks */, 7A11471F23FEA18700B424AF /* RxRelay in Frameworks */,
7AF58D2F24029C5200CE01A0 /* MagazineLayout in Frameworks */, 7AF58D2F24029C5200CE01A0 /* MagazineLayout in Frameworks */,
7A530B78240010D900CBFE6E /* InputMask in Frameworks */,
7A11471D23FEA18700B424AF /* RxSwift in Frameworks */, 7A11471D23FEA18700B424AF /* RxSwift in Frameworks */,
7A11472623FEA1F400B424AF /* Realm in Frameworks */, 7A11472623FEA1F400B424AF /* Realm in Frameworks */,
); );
@ -285,6 +289,7 @@
7A6DD90724329144009DE740 /* CenterTextLayer.swift */, 7A6DD90724329144009DE740 /* CenterTextLayer.swift */,
7A96AE32246C095700297C33 /* Base64FS.swift */, 7A96AE32246C095700297C33 /* Base64FS.swift */,
7A15051124DB3E3000F39631 /* AnyEncodable.swift */, 7A15051124DB3E3000F39631 /* AnyEncodable.swift */,
7ADF6C9C250FA96000F237B2 /* SwiftMaskTextfield.swift */,
); );
path = ThirdParty; path = ThirdParty;
sourceTree = "<group>"; sourceTree = "<group>";
@ -404,6 +409,7 @@
7AB67E8B2435C38700258F61 /* CustomTextField.swift */, 7AB67E8B2435C38700258F61 /* CustomTextField.swift */,
7AB67E8D2435D1A000258F61 /* CustomButton.swift */, 7AB67E8D2435D1A000258F61 /* CustomButton.swift */,
7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */, 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */,
7ADF6C96250F41B000F237B2 /* PNKeyboard.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -411,6 +417,7 @@
7A6DD90424326788009DE740 /* Fonts */ = { 7A6DD90424326788009DE740 /* Fonts */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7ADF6C98250F872C00F237B2 /* RoadNumbers.otf */,
7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */, 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */,
); );
path = Fonts; path = Fonts;
@ -451,7 +458,6 @@
7A11472223FEA18700B424AF /* RxBlocking */, 7A11472223FEA18700B424AF /* RxBlocking */,
7A11472523FEA1F400B424AF /* Realm */, 7A11472523FEA1F400B424AF /* Realm */,
7A11472723FEA1F400B424AF /* RealmSwift */, 7A11472723FEA1F400B424AF /* RealmSwift */,
7A530B77240010D900CBFE6E /* InputMask */,
7A530B8A240181F500CBFE6E /* RxRealm */, 7A530B8A240181F500CBFE6E /* RxRealm */,
7AF58D2E24029C5200CE01A0 /* MagazineLayout */, 7AF58D2E24029C5200CE01A0 /* MagazineLayout */,
7AF58D332402A91C00CE01A0 /* Kingfisher */, 7AF58D332402A91C00CE01A0 /* Kingfisher */,
@ -492,7 +498,6 @@
packageReferences = ( packageReferences = (
7A11471B23FEA18700B424AF /* XCRemoteSwiftPackageReference "RxSwift" */, 7A11471B23FEA18700B424AF /* XCRemoteSwiftPackageReference "RxSwift" */,
7A11472423FEA1F400B424AF /* XCRemoteSwiftPackageReference "realm-cocoa" */, 7A11472423FEA1F400B424AF /* XCRemoteSwiftPackageReference "realm-cocoa" */,
7A530B76240010D900CBFE6E /* XCRemoteSwiftPackageReference "input-mask-ios" */,
7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */, 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */,
7AF58D2D24029C5200CE01A0 /* XCRemoteSwiftPackageReference "MagazineLayout" */, 7AF58D2D24029C5200CE01A0 /* XCRemoteSwiftPackageReference "MagazineLayout" */,
7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */, 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */,
@ -515,6 +520,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7ADF6C99250F872C00F237B2 /* RoadNumbers.otf in Resources */,
7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */, 7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */,
7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */, 7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */,
7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */, 7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */,
@ -537,6 +543,7 @@
7A96AE31246B2FE400297C33 /* Constants.swift in Sources */, 7A96AE31246B2FE400297C33 /* Constants.swift in Sources */,
7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */, 7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */,
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */, 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
7ADF6C9D250FA96000F237B2 /* SwiftMaskTextfield.swift in Sources */,
7A27ADF924A09CAD0035F39E /* CocoaError.swift in Sources */, 7A27ADF924A09CAD0035F39E /* CocoaError.swift in Sources */,
7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */, 7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */,
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */, 7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
@ -600,6 +607,7 @@
7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */, 7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */,
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */, 7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */, 7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */,
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
7A000AA224C2EEDE001F5B00 /* Location.swift in Sources */, 7A000AA224C2EEDE001F5B00 /* Location.swift in Sources */,
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */, 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */,
); );
@ -841,14 +849,6 @@
minimumVersion = 5.0.0; minimumVersion = 5.0.0;
}; };
}; };
7A530B76240010D900CBFE6E /* XCRemoteSwiftPackageReference "input-mask-ios" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/RedMadRobot/input-mask-ios";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 5.0.0;
};
};
7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */ = { 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/RxSwiftCommunity/RxRealm"; repositoryURL = "https://github.com/RxSwiftCommunity/RxRealm";
@ -937,11 +937,6 @@
package = 7A11472423FEA1F400B424AF /* XCRemoteSwiftPackageReference "realm-cocoa" */; package = 7A11472423FEA1F400B424AF /* XCRemoteSwiftPackageReference "realm-cocoa" */;
productName = RealmSwift; productName = RealmSwift;
}; };
7A530B77240010D900CBFE6E /* InputMask */ = {
isa = XCSwiftPackageProductDependency;
package = 7A530B76240010D900CBFE6E /* XCRemoteSwiftPackageReference "input-mask-ios" */;
productName = InputMask;
};
7A530B8A240181F500CBFE6E /* RxRealm */ = { 7A530B8A240181F500CBFE6E /* RxRealm */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */; package = 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */;

View File

@ -19,22 +19,13 @@
"version": "1.1.0" "version": "1.1.0"
} }
}, },
{
"package": "InputMask",
"repositoryURL": "https://github.com/RedMadRobot/input-mask-ios",
"state": {
"branch": null,
"revision": "78a98f31d5b7fe2c0d84146748f22114c1c17c7c",
"version": "5.0.0"
}
},
{ {
"package": "Kingfisher", "package": "Kingfisher",
"repositoryURL": "https://github.com/onevcat/Kingfisher", "repositoryURL": "https://github.com/onevcat/Kingfisher",
"state": { "state": {
"branch": null, "branch": null,
"revision": "1339ebea9498ef6c3fc75cc195d7163d7c7167f9", "revision": "175eeb4618b0a6ef4d69a7409b6a74ddd235a093",
"version": "5.14.1" "version": "5.15.0"
} }
}, },
{ {
@ -42,8 +33,8 @@
"repositoryURL": "https://github.com/airbnb/MagazineLayout", "repositoryURL": "https://github.com/airbnb/MagazineLayout",
"state": { "state": {
"branch": null, "branch": null,
"revision": "12dd2cc84b7f17c4f46c7d95cde64d521c588ee8", "revision": "4a5eff2203ad8d8c7e14ea1b283b64f9320752a9",
"version": "1.6.1" "version": "1.6.2"
} }
}, },
{ {
@ -51,8 +42,8 @@
"repositoryURL": "https://github.com/realm/realm-cocoa", "repositoryURL": "https://github.com/realm/realm-cocoa",
"state": { "state": {
"branch": null, "branch": null,
"revision": "5d296d8a3750ac86e731263b44e2ba0c3c85f2cb", "revision": "6310911a38d55003f0b8acca96635aa2bfc3e2c0",
"version": "5.3.2" "version": "5.4.0"
} }
}, },
{ {
@ -60,8 +51,8 @@
"repositoryURL": "https://github.com/realm/realm-core", "repositoryURL": "https://github.com/realm/realm-core",
"state": { "state": {
"branch": null, "branch": null,
"revision": "55a0d32516c0f3a5fd3ab8c0224ee22efcc7cd58", "revision": "34d7fbcbab2a94bcad081bddf9d172961cd84659",
"version": "6.0.12" "version": "6.0.25"
} }
}, },
{ {

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,28 @@
{
"colors" : [
{
"color" : {
"platform" : "ios",
"reference" : "systemGray2Color"
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"platform" : "ios",
"reference" : "systemGray3Color"
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.300",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -328,14 +328,14 @@
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes> <prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="VehicleCell" id="VEP-QD-i6y" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="85"/> <rect key="frame" x="0.0" y="28" width="375" height="85.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VEP-QD-i6y" id="8hH-8I-XLB">
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/> <rect key="frame" x="0.0" y="0.0" width="375" height="85.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Kia (JF) Optima" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AQY-7N-q8D">
<rect key="frame" x="8" y="8" width="124" height="21"/> <rect key="frame" x="8" y="8" width="124" height="21.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/> <fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -347,7 +347,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target"> <view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="8" y="37" width="317" height="40"/> <rect key="frame" x="8" y="37.5" width="317" height="40"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/> <constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/>
@ -596,18 +596,14 @@
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="Peq-Zq-kNT"> <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"/> <rect key="frame" x="62.5" y="68" width="250" height="114.5"/>
<subviews> <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="CustomTextField" customModule="AutoCat" customModuleProvider="target"> <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"/> <rect key="frame" x="0.0" y="0.0" width="250" height="50.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/> <fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
<textInputTraits key="textInputTraits" returnKeyType="done"/> <textInputTraits key="textInputTraits" returnKeyType="done"/>
<userDefinedRuntimeAttributes> <connections>
<userDefinedRuntimeAttribute type="number" keyPath="borderWidth"> <action selector="textFieldDidBeginEditing:" destination="twc-qR-t1G" eventType="editingDidBegin" id="Kvu-xK-QuZ"/>
<real key="value" value="1"/> <action selector="textFieldDidChange:" destination="twc-qR-t1G" eventType="editingChanged" id="x69-Ym-Zdm"/>
</userDefinedRuntimeAttribute> </connections>
<userDefinedRuntimeAttribute type="number" keyPath="cornerRadius">
<real key="value" value="6"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
</textField> </textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ync-fd-xQI" customClass="CustomButton" customModule="AutoCat" customModuleProvider="target"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" 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"/> <rect key="frame" x="0.0" y="66.5" width="250" height="48"/>
@ -696,7 +692,7 @@
<connections> <connections>
<outlet property="check" destination="Ync-fd-xQI" id="oxC-p6-Mou"/> <outlet property="check" destination="Ync-fd-xQI" id="oxC-p6-Mou"/>
<outlet property="history" destination="JKr-UE-x8f" id="GP9-Gz-WBm"/> <outlet property="history" destination="JKr-UE-x8f" id="GP9-Gz-WBm"/>
<outlet property="number" destination="8FU-Gy-4MU" id="cCY-vV-Mdy"/> <outlet property="number" destination="8FU-Gy-4MU" id="PwP-Pn-eeO"/>
</connections> </connections>
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="h7Y-mR-2Ti" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="h7Y-mR-2Ti" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
@ -766,6 +762,7 @@
<segue destination="RK6-pn-2Bg" kind="relationship" relationship="viewControllers" id="KNz-WF-Kyy"/> <segue destination="RK6-pn-2Bg" kind="relationship" relationship="viewControllers" id="KNz-WF-Kyy"/>
<segue destination="GCa-Re-j14" kind="relationship" relationship="viewControllers" id="FGp-f6-fUh"/> <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"/> <segue destination="4jU-Z3-PF2" kind="relationship" relationship="viewControllers" id="aH2-IT-86l"/>
<segue destination="5k8-Cz-8yP" kind="relationship" relationship="viewControllers" id="w22-e0-uc5"/>
</connections> </connections>
</tabBarController> </tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
@ -891,6 +888,33 @@
</objects> </objects>
<point key="canvasLocation" x="199" y="143"/> <point key="canvasLocation" x="199" y="143"/>
</scene> </scene>
<!--View Controller-->
<scene sceneID="akP-Pw-M4Q">
<objects>
<viewController id="nFZ-L6-cSY" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="qu5-Ue-dlv">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="q5m-0R-HGC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</mapView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="q5m-0R-HGC" firstAttribute="top" secondItem="qu5-Ue-dlv" secondAttribute="top" id="7pI-P3-wL4"/>
<constraint firstItem="q5m-0R-HGC" firstAttribute="leading" secondItem="qu5-Ue-dlv" secondAttribute="leading" id="P1e-Ua-97P"/>
<constraint firstAttribute="bottom" secondItem="q5m-0R-HGC" secondAttribute="bottom" id="rrd-Z6-g8h"/>
<constraint firstAttribute="trailing" secondItem="q5m-0R-HGC" secondAttribute="trailing" id="s5w-az-6e7"/>
</constraints>
<viewLayoutGuide key="safeArea" id="gve-eU-fKS"/>
</view>
<navigationItem key="navigationItem" id="jf2-Du-fcn"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XB6-0a-b8N" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4200.8000000000002" y="-1374.0629685157423"/>
</scene>
<!--Check--> <!--Check-->
<scene sceneID="pUX-kf-oY1"> <scene sceneID="pUX-kf-oY1">
<objects> <objects>
@ -966,12 +990,32 @@
</objects> </objects>
<point key="canvasLocation" x="3261.5999999999999" y="1660.1199400299852"/> <point key="canvasLocation" x="3261.5999999999999" y="1660.1199400299852"/>
</scene> </scene>
<!--Map-->
<scene sceneID="XqL-ef-b4J">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="5k8-Cz-8yP" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Map" image="map" catalog="system" id="emU-RO-5HQ"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="0UP-PJ-Rc3">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="nFZ-L6-cSY" kind="relationship" relationship="rootViewController" id="iQf-3h-ziw"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="THp-Fz-ftx" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3261.5999999999999" y="-1374.0629685157423"/>
</scene>
</scenes> </scenes>
<resources> <resources>
<image name="check" width="25" height="25"/> <image name="check" width="25" height="25"/>
<image name="check-compact" width="18" height="18"/> <image name="check-compact" width="18" height="18"/>
<image name="doc.on.doc" catalog="system" width="117" height="128"/> <image name="doc.on.doc" catalog="system" width="117" height="128"/>
<image name="line.horizontal.3.decrease" catalog="system" width="128" height="73"/> <image name="line.horizontal.3.decrease" catalog="system" width="128" height="73"/>
<image name="map" catalog="system" width="128" height="113"/>
<image name="play.fill" catalog="system" width="116" height="128"/> <image name="play.fill" catalog="system" width="116" height="128"/>
<image name="record" width="31" height="31"/> <image name="record" width="31" height="31"/>
<image name="record-compact" width="23" height="23"/> <image name="record-compact" width="23" height="23"/>

View File

@ -1,5 +1,4 @@
import UIKit import UIKit
import InputMask
import RealmSwift import RealmSwift
import RxSwift import RxSwift
import SwiftDate import SwiftDate
@ -12,14 +11,13 @@ enum EventAction {
case sendSpecific(VehicleEvent) case sendSpecific(VehicleEvent)
} }
class CheckController: UIViewController, MaskedTextFieldDelegateListener, UITableViewDelegate { class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegate, PNKeyboardDelegate {
@IBOutlet weak var number: UITextField! @IBOutlet weak var number: SwiftMaskTextfield!
@IBOutlet weak var check: UIButton! @IBOutlet weak var check: UIButton!
@IBOutlet weak var history: UITableView! @IBOutlet weak var history: UITableView!
let bag = DisposeBag() let bag = DisposeBag()
let maskFieldDelegate = MaskedTextFieldDelegate()
// MARK: - Lifecycle // MARK: - Lifecycle
@ -29,9 +27,10 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener, UITabl
guard let realm = try? Realm() else { return } guard let realm = try? Realm() else { return }
self.hideKeyboardWhenTappedAround() self.hideKeyboardWhenTappedAround()
self.maskFieldDelegate.primaryMaskFormat = "[A][000][AA] [009]" let keyboard = PNKeyboard(target: self.number)
self.maskFieldDelegate.listener = self keyboard.delegate = self
self.number.delegate = self.maskFieldDelegate self.number.formatPattern = "@###@@###"
self.number.inputView = keyboard
self.check.isEnabled = false self.check.isEnabled = false
let ds = RxTableViewSectionedAnimatedDataSource<DateSection<Vehicle>>(configureCell: { dataSource, tableView, indexPath, item in let ds = RxTableViewSectionedAnimatedDataSource<DateSection<Vehicle>>(configureCell: { dataSource, tableView, indexPath, item in
@ -211,19 +210,25 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener, UITabl
// MARK: - UITextFieldDelegate // MARK: - UITextFieldDelegate
func textField(_ textField: UITextField, didFillMandatoryCharacters complete: Bool, didExtractValue value: String) { @IBAction func textFieldDidBeginEditing(_ textField: UITextField) {
self.check.isEnabled = complete LocationManager.requestCurrentLocation().subscribe().disposed(by: self.bag)
} }
func textFieldShouldReturn(_ textField: UITextField) -> Bool { @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 { if self.check.isEnabled {
self.checkTapped(self.check) self.checkTapped(self.check)
} }
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
LocationManager.requestCurrentLocation().subscribe().disposed(by: self.bag)
} }
// MARK: - // MARK: -

View File

@ -19,7 +19,7 @@ class RecordsController: UIViewController, UITableViewDelegate {
var recordDisposable: Disposable? var recordDisposable: Disposable?
var audioSessionObserver: NSObjectProtocol? var audioSessionObserver: NSObjectProtocol?
let validLetters = ["А", "В", "Е", "К", "М", "Н", "О", "Р", "С", "Т", "У", "Х"] let validLetters = Constants.pnLettersMap.keys.map(String.init)
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()

Binary file not shown.

View File

@ -52,6 +52,7 @@
<string>Access is needed for recognizing plate numbers from voice recordings</string> <string>Access is needed for recognizing plate numbers from voice recordings</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>RoadNumbers.otf</string>
<string>RoadNumbers2.0.otf</string> <string>RoadNumbers2.0.otf</string>
</array> </array>
<key>UIApplicationSceneManifest</key> <key>UIApplicationSceneManifest</key>

View File

@ -1,16 +1,12 @@
import Foundation import Foundation
class PlateNumber { class PlateNumber {
private static let lettersMap: [Character: Character] = [
"А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X"
]
private var number: String private var number: String
private var numberEnglish: String private var numberEnglish: String
init(_ string: String) { init(_ string: String) {
self.number = string self.number = string
self.numberEnglish = String(self.number.map { PlateNumber.lettersMap[$0] ?? $0 }) self.numberEnglish = String(self.number.map { Constants.pnLettersMap[$0] ?? $0 })
} }
func asString() -> String { func asString() -> String {
@ -18,12 +14,12 @@ class PlateNumber {
} }
func mainPart() -> String { func mainPart() -> String {
let index = self.numberEnglish.startIndex(offsetBy: 6) let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
return String(self.numberEnglish[..<index]) return String(self.numberEnglish[..<index])
} }
func region() -> String { func region() -> String {
let index = self.numberEnglish.startIndex(offsetBy: 6) let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
return String(self.numberEnglish[index...]) return String(self.numberEnglish[index...])
} }
} }

View File

@ -0,0 +1,269 @@
/*
* MIT License
*
* Copyright (c) 2016 Gabriel Maccori Kozma
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import UIKit
//**********************************************************************************************************
//
// MARK: - Constants -
//
//**********************************************************************************************************
//**********************************************************************************************************
//
// MARK: - Definitions -
//
//**********************************************************************************************************
//**********************************************************************************************************
//
// MARK: - Class -
//
//**********************************************************************************************************
open class SwiftMaskTextfield : UITextField {
//**************************************************
// MARK: - Properties
//**************************************************
public let lettersAndDigitsReplacementChar: String = "*"
public let anyLetterReplecementChar: String = "@"
public let lowerCaseLetterReplecementChar: String = "a"
public let upperCaseLetterReplecementChar: String = "A"
public let digitsReplecementChar: String = "#"
/**
Var that holds the format pattern that you wish to apply
to some text
If the pattern is set to "" no mask would be applied and
the textfield would behave like a normal one
*/
@IBInspectable open var formatPattern: String = ""
/**
Var that holds the prefix to be added to the textfield
If the prefix is set to "" no string will be added to the beggining
of the text
*/
@IBInspectable open var prefix: String = ""
/**
Var that have the maximum length, based on the mask set
*/
open var maxLength: Int {
get {
return formatPattern.count + prefix.count
}
}
/**
Overriding the var text from UITextField so if any text
is applied programmatically by calling formatText
*/
override open var text: String? {
set {
super.text = newValue
self.formatText()
}
get {
return super.text
}
}
//**************************************************
// MARK: - Constructors
//**************************************************
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
public override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
deinit {
self.deRegisterForNotifications()
}
//**************************************************
// MARK: - Private Methods
//**************************************************
fileprivate func setup() {
self.registerForNotifications()
}
fileprivate func registerForNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(textDidChange),
name: NSNotification.Name(rawValue: "UITextFieldTextDidChangeNotification"),
object: self)
}
fileprivate func deRegisterForNotifications() {
NotificationCenter.default.removeObserver(self)
}
@objc fileprivate func textDidChange() {
self.undoManager?.removeAllActions()
self.formatText()
}
fileprivate func getOnlyDigitsString(_ string: String) -> String {
let charactersArray = string.components(separatedBy: CharacterSet.decimalDigits.inverted)
return charactersArray.joined(separator: "")
}
fileprivate func getOnlyLettersString(_ string: String) -> String {
let charactersArray = string.components(separatedBy: CharacterSet.letters.inverted)
return charactersArray.joined(separator: "")
}
fileprivate func getUppercaseLettersString(_ string: String) -> String {
let charactersArray = string.components(separatedBy: CharacterSet.uppercaseLetters.inverted)
return charactersArray.joined(separator: "")
}
fileprivate func getLowercaseLettersString(_ string: String) -> String {
let charactersArray = string.components(separatedBy: CharacterSet.lowercaseLetters.inverted)
return charactersArray.joined(separator: "")
}
fileprivate func getFilteredString(_ string: String) -> String {
let charactersArray = string.components(separatedBy: CharacterSet.alphanumerics.inverted)
return charactersArray.joined(separator: "")
}
fileprivate func getStringWithoutPrefix(_ string: String) -> String {
if string.range(of: self.prefix) != nil {
if string.count > self.prefix.count {
let prefixIndex = string.index(string.endIndex, offsetBy: (string.count - self.prefix.count) * -1)
return String(string[prefixIndex...])
} else if string.count == self.prefix.count {
return ""
}
}
return string
}
//**************************************************
// MARK: - Self Public Methods
//**************************************************
/**
Func that formats the text based on formatPattern
Override this function if you want to customize the behaviour of
the class
*/
open func formatText() {
var currentTextForFormatting = ""
if let text = super.text {
if text.count > 0 {
currentTextForFormatting = self.getStringWithoutPrefix(text)
}
}
if self.maxLength > 0 {
var formatterIndex = self.formatPattern.startIndex, currentTextForFormattingIndex = currentTextForFormatting.startIndex
var finalText = ""
currentTextForFormatting = self.getFilteredString(currentTextForFormatting)
if currentTextForFormatting.count > 0 {
while true {
let formatPatternRange = formatterIndex ..< formatPattern.index(after: formatterIndex)
let currentFormatCharacter = String(self.formatPattern[formatPatternRange])
let currentTextForFormattingPatterRange = currentTextForFormattingIndex ..< currentTextForFormatting.index(after: currentTextForFormattingIndex)
let currentTextForFormattingCharacter = String(currentTextForFormatting[currentTextForFormattingPatterRange])
switch currentFormatCharacter {
case self.lettersAndDigitsReplacementChar:
finalText += currentTextForFormattingCharacter
currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex)
formatterIndex = formatPattern.index(after: formatterIndex)
case self.anyLetterReplecementChar:
let filteredChar = self.getOnlyLettersString(currentTextForFormattingCharacter)
if !filteredChar.isEmpty {
finalText += filteredChar
formatterIndex = formatPattern.index(after: formatterIndex)
}
currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex)
case self.lowerCaseLetterReplecementChar:
let filteredChar = self.getLowercaseLettersString(currentTextForFormattingCharacter)
if !filteredChar.isEmpty {
finalText += filteredChar
formatterIndex = formatPattern.index(after: formatterIndex)
}
currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex)
case self.upperCaseLetterReplecementChar:
let filteredChar = self.getUppercaseLettersString(currentTextForFormattingCharacter)
if !filteredChar.isEmpty {
finalText += filteredChar
formatterIndex = formatPattern.index(after: formatterIndex)
}
currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex)
case self.digitsReplecementChar:
let filteredChar = self.getOnlyDigitsString(currentTextForFormattingCharacter)
if !filteredChar.isEmpty {
finalText += filteredChar
formatterIndex = formatPattern.index(after: formatterIndex)
}
currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex)
default:
finalText += currentFormatCharacter
formatterIndex = formatPattern.index(after: formatterIndex)
}
if formatterIndex >= self.formatPattern.endIndex ||
currentTextForFormattingIndex >= currentTextForFormatting.endIndex {
break
}
}
}
if finalText.count > 0 {
super.text = "\(self.prefix)\(finalText)"
} else {
super.text = finalText
}
if let text = self.text {
if text.count > self.maxLength {
super.text = String(text[text.index(text.startIndex, offsetBy: self.maxLength)])
}
}
}
}
}

View File

@ -3,14 +3,18 @@ import Foundation
enum Constants { enum Constants {
static var baseUrl: String { static var baseUrl: String {
#if DEBUG #if DEBUG
return "http://127.0.0.1:3000/" //return "http://127.0.0.1:3000/"
//return "http://192.168.1.67:3000/" //return "http://192.168.1.67:3000/"
//return "https://vps.aliencat.pro:8443/" return "https://vps.aliencat.pro:8443/"
#else #else
return "https://vps.aliencat.pro:8443/" return "https://vps.aliencat.pro:8443/"
#endif #endif
} }
static let pnLettersMap: [Character: Character] = [
"А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X"
]
static let googleAuthURL = "https://accounts.google.com/o/oauth2/v2/auth" static let googleAuthURL = "https://accounts.google.com/o/oauth2/v2/auth"
static let googleTokenURL = "https://oauth2.googleapis.com/token" static let googleTokenURL = "https://oauth2.googleapis.com/token"
static let googleRedirectURL = "com.googleusercontent.apps.994679674451-k7clunkk4nicl6iuajdtc5u7hvustbdb:/oauth2callback" static let googleRedirectURL = "com.googleusercontent.apps.994679674451-k7clunkk4nicl6iuajdtc5u7hvustbdb:/oauth2callback"

View File

@ -0,0 +1,201 @@
import UIKit
protocol PNKeyboardDelegate: class {
func returnClicked()
}
protocol PNButtonDelegate: class {
func buttonTapped(_ button: PNButton)
}
enum PNButtonType {
case symbol(String)
case backspace
case done
}
class PNButton: UIButton {
private(set) var type: PNButtonType
private var timer: Timer?
private var waitCount = 0
weak var delegate: PNButtonDelegate?
init(letter: Character) {
self.type = .symbol(String(letter))
super.init(frame: .zero)
self.titleLabel?.font = UIFont(name: "RoadNumbers", size: 36)
let title = String(Constants.pnLettersMap[letter] ?? letter)
self.setTitle(title, for: .normal)
self.setTitleColor(.label, for: .normal)
self.backgroundColor = UIColor(named: "KeyBackground")
self.setup()
}
init(digit: Int) {
self.type = .symbol(String(digit))
super.init(frame: .zero)
self.titleLabel?.font = UIFont(name: "RoadNumbersCyr-Regular", size: 30)
let character = Character(String(digit))
let title = String(Constants.pnLettersMap[character] ?? character)
self.setTitle(title, for: .normal)
self.setTitleColor(.label, for: .normal)
self.backgroundColor = UIColor(named: "KeyBackground")
self.setup()
}
init(imageName: String, type: PNButtonType) {
self.type = type
super.init(frame: .zero)
self.setImage(UIImage(systemName: imageName), for: .normal)
self.backgroundColor = UIColor(named: "DarkKeyBackground")
self.tintColor = .label
self.setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup(_ radius: CGFloat = 0.5) {
self.layer.cornerRadius = 6
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 0.4
self.layer.shadowOffset = CGSize(width: 0.0, height : 1.0)
self.layer.shadowRadius = radius
self.addTarget(self, action: #selector(buttonDown), for: .touchDown)
self.addTarget(self, action: #selector(buttonUp), for: [.touchUpInside, .touchUpOutside])
self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside)
}
@objc func buttonDown(_ sender: PNButton) {
print("buttonDown")
sender.layer.opacity = 0.3
self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
self.waitCount += 1
if self.waitCount > 5 {
self.delegate?.buttonTapped(self)
}
})
}
@objc func buttonUp(_ sender: PNButton) {
print("buttonUp")
sender.layer.opacity = 1
self.timer?.invalidate()
self.timer = nil
self.waitCount = 0
}
@objc func touchUpInside(_ sender: PNButton) {
print("touchUpInside")
self.delegate?.buttonTapped(self)
}
}
class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate {
private weak var target: UIKeyInput?
weak var delegate: PNKeyboardDelegate?
init(target: UIKeyInput) {
self.target = target
super.init(frame: .zero)
self.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let blurEffectView = UIVisualEffectView(effect: UIBlurEffect())
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(blurEffectView)
self.setupButtons()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupButtons() {
let letters: [PNButton] = Constants.pnLettersMap.keys.sorted().map { letter in
let button = PNButton(letter: letter)
button.delegate = self
return button
}
let digits: [PNButton] = [1,2,3,4,5,6,7,8,9,0].map { digit in
let button = PNButton(digit: digit)
button.delegate = self
return button
}
let backspace = PNButton(imageName: "delete.left", type: .backspace)
backspace.delegate = self
let done = PNButton(imageName: "arrow.turn.down.left", type: .done)
done.delegate = self
done.backgroundColor = .systemBlue
done.tintColor = .white
let letterRows = [
self.createLetterStack([letters[0], letters[1], letters[2]]),
self.createLetterStack([letters[3], letters[4], letters[5]]),
self.createLetterStack([letters[6], letters[7], letters[8]]),
self.createLetterStack([letters[9], letters[10], letters[11]])
]
let lettersStack = UIStackView(arrangedSubviews: letterRows)
lettersStack.axis = .vertical
lettersStack.distribution = .fillEqually
lettersStack.spacing = 8
let digitRows = [
self.createLetterStack([digits[0], digits[1], digits[2]]),
self.createLetterStack([digits[3], digits[4], digits[5]]),
self.createLetterStack([digits[6], digits[7], digits[8]]),
self.createLetterStack([digits[9], backspace, done])
]
let digitsStack = UIStackView(arrangedSubviews: digitRows)
digitsStack.axis = .vertical
digitsStack.distribution = .fillEqually
digitsStack.spacing = 8
let mainStack = UIStackView(arrangedSubviews: [lettersStack, digitsStack])
mainStack.spacing = 16
mainStack.distribution = .fillEqually
mainStack.translatesAutoresizingMaskIntoConstraints = false
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)
])
}
func createLetterStack(_ views: [UIView]) -> UIStackView {
let stack = UIStackView(arrangedSubviews: views)
stack.distribution = .fillEqually
stack.spacing = 8
return stack
}
// MARK: - PNButtonDelegate
func buttonTapped(_ button: PNButton) {
UIDevice.current.playInputClick()
switch button.type {
case .symbol(let s):
self.target?.insertText(s)
case .backspace:
self.target?.deleteBackward()
case .done:
self.delegate?.returnClicked()
}
}
// MARK: - UIInputViewAudioFeedback
var enableInputClicksWhenVisible: Bool {
return true
}
}