Add view for drawing realistic plate numbers

This commit is contained in:
Selim Mustafaev 2020-04-01 19:44:11 +03:00
parent 47801fa56d
commit 9d1efd8d0d
15 changed files with 411 additions and 27 deletions

View File

@ -41,6 +41,10 @@
7A530B88240180CC00CBFE6E /* RxTableViewRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B84240180CC00CBFE6E /* RxTableViewRealmDataSource.swift */; }; 7A530B88240180CC00CBFE6E /* RxTableViewRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B84240180CC00CBFE6E /* RxTableViewRealmDataSource.swift */; };
7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */ = {isa = PBXBuildFile; productRef = 7A530B8A240181F500CBFE6E /* RxRealm */; }; 7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */ = {isa = PBXBuildFile; productRef = 7A530B8A240181F500CBFE6E /* RxRealm */; };
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD902242BF4A5009DE740 /* PlateView.swift */; }; 7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD902242BF4A5009DE740 /* PlateView.swift */; };
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90724329144009DE740 /* CenterTextLayer.swift */; };
7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */; };
7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90B24335A6D009DE740 /* FlagLayer.swift */; };
7A6DD90E24337930009DE740 /* PlateNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90D24337930009DE740 /* PlateNumber.swift */; };
7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; }; 7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; };
7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; }; 7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; };
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; }; 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; };
@ -77,6 +81,10 @@
7A530B83240180CC00CBFE6E /* Reactive+RxRealmDataSources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+RxRealmDataSources.swift"; sourceTree = "<group>"; }; 7A530B83240180CC00CBFE6E /* Reactive+RxRealmDataSources.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+RxRealmDataSources.swift"; sourceTree = "<group>"; };
7A530B84240180CC00CBFE6E /* RxTableViewRealmDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewRealmDataSource.swift; sourceTree = "<group>"; }; 7A530B84240180CC00CBFE6E /* RxTableViewRealmDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RxTableViewRealmDataSource.swift; sourceTree = "<group>"; };
7A6DD902242BF4A5009DE740 /* PlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = "<group>"; }; 7A6DD902242BF4A5009DE740 /* PlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = "<group>"; };
7A6DD90724329144009DE740 /* CenterTextLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTextLayer.swift; sourceTree = "<group>"; };
7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = "<group>"; };
7A6DD90B24335A6D009DE740 /* FlagLayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlagLayer.swift; sourceTree = "<group>"; };
7A6DD90D24337930009DE740 /* PlateNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = "<group>"; };
7A7547DB2403180A004E8406 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = "<group>"; }; 7A7547DB2403180A004E8406 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = "<group>"; };
7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; }; 7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = "<group>"; };
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; }; 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = "<group>"; };
@ -133,6 +141,7 @@
7A1146FF23FDE7E500B424AF /* AutoCat */ = { 7A1146FF23FDE7E500B424AF /* AutoCat */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A6DD90424326788009DE740 /* Fonts */,
7A6DD901242BF48D009DE740 /* Views */, 7A6DD901242BF48D009DE740 /* Views */,
7A530B7C24017FBE00CBFE6E /* Cells */, 7A530B7C24017FBE00CBFE6E /* Cells */,
7A11474523FF2A9000B424AF /* Models */, 7A11474523FF2A9000B424AF /* Models */,
@ -165,6 +174,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A530B7B24017DED00CBFE6E /* RxRealmDataSources */, 7A530B7B24017DED00CBFE6E /* RxRealmDataSources */,
7A6DD90724329144009DE740 /* CenterTextLayer.swift */,
); );
path = ThirdParty; path = ThirdParty;
sourceTree = "<group>"; sourceTree = "<group>";
@ -185,6 +195,7 @@
7A11474A23FF368B00B424AF /* Settings.swift */, 7A11474A23FF368B00B424AF /* Settings.swift */,
7A530B7F2401803A00CBFE6E /* Vehicle.swift */, 7A530B7F2401803A00CBFE6E /* Vehicle.swift */,
7A0516192414FF0900FC55AC /* DateSection.swift */, 7A0516192414FF0900FC55AC /* DateSection.swift */,
7A6DD90D24337930009DE740 /* PlateNumber.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -226,10 +237,19 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A6DD902242BF4A5009DE740 /* PlateView.swift */, 7A6DD902242BF4A5009DE740 /* PlateView.swift */,
7A6DD90B24335A6D009DE740 /* FlagLayer.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A6DD90424326788009DE740 /* Fonts */ = {
isa = PBXGroup;
children = (
7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */,
);
path = Fonts;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -318,6 +338,7 @@
files = ( files = (
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 */,
7A11470A23FDE7E600B424AF /* Assets.xcassets in Resources */, 7A11470A23FDE7E600B424AF /* Assets.xcassets in Resources */,
7A11470823FDE7E500B424AF /* Main.storyboard in Resources */, 7A11470823FDE7E500B424AF /* Main.storyboard in Resources */,
); );
@ -355,6 +376,9 @@
7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */, 7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */,
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */, 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
7A530B86240180CC00CBFE6E /* RealmBindObserver.swift in Sources */, 7A530B86240180CC00CBFE6E /* RealmBindObserver.swift in Sources */,
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */,
7A6DD90E24337930009DE740 /* PlateNumber.swift in Sources */,
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */, 7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */,
7A11474923FF2B2D00B424AF /* Response.swift in Sources */, 7A11474923FF2B2D00B424AF /* Response.swift in Sources */,
7A11471823FDEBFA00B424AF /* ReportController.swift in Sources */, 7A11471823FDEBFA00B424AF /* ReportController.swift in Sources */,

View File

@ -8,7 +8,7 @@
BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint"> BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint">
<BreakpointContent <BreakpointContent
uuid = "C14D0996-5708-44D2-A6BA-4A4B50B522EE" uuid = "C14D0996-5708-44D2-A6BA-4A4B50B522EE"
shouldBeEnabled = "Yes" shouldBeEnabled = "No"
ignoreCount = "0" ignoreCount = "0"
continueAfterRunningActions = "No"> continueAfterRunningActions = "No">
</BreakpointContent> </BreakpointContent>
@ -17,7 +17,7 @@
BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint"> BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
<BreakpointContent <BreakpointContent
uuid = "CF01B44D-372B-4C78-A197-7FDEC607CE0E" uuid = "CF01B44D-372B-4C78-A197-7FDEC607CE0E"
shouldBeEnabled = "Yes" shouldBeEnabled = "No"
ignoreCount = "0" ignoreCount = "0"
continueAfterRunningActions = "No" continueAfterRunningActions = "No"
scope = "1" scope = "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" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.152",
"green" : "0.152",
"red" : "0.152"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="dark"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16086"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
@ -239,7 +239,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<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="105.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"> <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="A001AA 777" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8FU-Gy-4MU">
<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"/>
@ -247,9 +247,20 @@
<textInputTraits key="textInputTraits" returnKeyType="done"/> <textInputTraits key="textInputTraits" returnKeyType="done"/>
</textField> </textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ync-fd-xQI"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ync-fd-xQI">
<rect key="frame" x="0.0" y="66.5" width="250" height="39"/> <rect key="frame" x="0.0" y="66.5" width="250" height="48"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle2"/> <color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<state key="normal" title="Check"/> <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>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="6"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections> <connections>
<action selector="checkTapped:" destination="twc-qR-t1G" eventType="touchUpInside" id="WiK-lL-7q7"/> <action selector="checkTapped:" destination="twc-qR-t1G" eventType="touchUpInside" id="WiK-lL-7q7"/>
</connections> </connections>
@ -260,30 +271,30 @@
</constraints> </constraints>
</stackView> </stackView>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="JKr-UE-x8f"> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" 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="197.5" width="375" height="420.5"/> <rect key="frame" x="0.0" y="206.5" width="375" height="411.5"/>
<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="3ON-lr-UlV" 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="3ON-lr-UlV" customClass="VehicleCell" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="375" height="95"/> <rect key="frame" x="0.0" y="28" width="375" height="85"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3ON-lr-UlV" id="IGw-UK-ebp"> <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="95"/> <rect key="frame" x="0.0" y="0.0" width="375" height="85"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="prz-7c-HiS"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="prz-7c-HiS">
<rect key="frame" x="8" y="8" width="359" height="79"/> <rect key="frame" x="8" y="8" width="359" height="69"/>
<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="h3N-1o-J20"> <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="h3N-1o-J20">
<rect key="frame" x="0.0" y="0.0" width="359" height="21"/> <rect key="frame" x="0.0" y="0.0" width="124" height="21"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/> <fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VHX-o0-3BP" customClass="PlateView" customModule="AutoCat" customModuleProvider="target"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="VHX-o0-3BP" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="29" width="359" height="50"/> <rect key="frame" x="0.0" y="29" width="359" height="40"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="50" id="vzo-oF-mWb"/> <constraint firstAttribute="height" constant="40" id="vzo-oF-mWb"/>
</constraints> </constraints>
</view> </view>
</subviews> </subviews>
@ -369,6 +380,7 @@
</textField> </textField>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ltG-B1-UBj"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ltG-B1-UBj">
<rect key="frame" x="0.0" y="100" width="262.5" height="30"/> <rect key="frame" x="0.0" y="100" width="262.5" height="30"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<state key="normal" title="Log in"/> <state key="normal" title="Log in"/>
<connections> <connections>
<action selector="loginTapped:" destination="pme-aR-UNJ" eventType="touchUpInside" id="qTu-4S-cqF"/> <action selector="loginTapped:" destination="pme-aR-UNJ" eventType="touchUpInside" id="qTu-4S-cqF"/>

View File

@ -11,8 +11,9 @@ class VehicleCell: UITableViewCell {
} }
func configure(with vehicle: Vehicle) { func configure(with vehicle: Vehicle) {
self.name.text = vehicle.brand?.name?.original self.name.text = vehicle.brand?.name?.original ?? "<unknown>"
self.plate.number = vehicle.number self.plate.number = PlateNumber(vehicle.number)
self.plate.unrecognized = vehicle.brand == nil
} }
} }

View File

@ -97,12 +97,19 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener {
@IBAction func checkTapped(_ sender: UIButton) { @IBAction func checkTapped(_ sender: UIButton) {
guard let number = self.number.text else { return } guard let number = self.number.text else { return }
let numberNormalized = number.filter { !$0.isWhitespace }.uppercased()
self.number.resignFirstResponder() self.number.resignFirstResponder()
self.number.text = nil self.number.text = nil
SVProgressHUD.show() SVProgressHUD.show()
Api.checkVehicle(by: number) Api.checkVehicle(by: numberNormalized)
.observeOn(MainScheduler.instance) .observeOn(MainScheduler.instance)
.subscribe(onNext: onReceivedVehicle(_:), onError: { err in .subscribe(onNext: onReceivedVehicle(_:), onError: { err in
if let realm = try? Realm() {
try? realm.write {
realm.add(Vehicle(numberNormalized))
}
}
SVProgressHUD.showError(withStatus: err.localizedDescription) SVProgressHUD.showError(withStatus: err.localizedDescription)
print(err.localizedDescription) print(err.localizedDescription)
}).disposed(by: self.bag) }).disposed(by: self.bag)

Binary file not shown.

View File

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIAppFonts</key>
<array>
<string>RoadNumbers2.0.otf</string>
</array>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>

View File

@ -0,0 +1,29 @@
import Foundation
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 numberEnglish: String
init(_ string: String) {
self.number = string
self.numberEnglish = String(self.number.map { PlateNumber.lettersMap[$0] ?? $0 })
}
func asString() -> String {
return self.number
}
func mainPart() -> String {
let index = self.numberEnglish.startIndex(offsetBy: 6)
return String(self.numberEnglish[..<index])
}
func region() -> String {
let index = self.numberEnglish.startIndex(offsetBy: 6)
return String(self.numberEnglish[index...])
}
}

View File

@ -108,4 +108,10 @@ class Vehicle: Object, Decodable, IdentifiableType {
required init() { required init() {
} }
init(_ number: String) {
self.number = number
self._id = UUID().uuidString
self.addedDate = Date().timeIntervalSince1970*1000
}
} }

View File

@ -0,0 +1,51 @@
//
// CenterTextLayer.swift
// CenterTextLayer
//
// Created by Cem Olcay on 12/04/2017.
//
// http://stackoverflow.com/a/41518502/2048130
//
#if os(iOS) || os(tvOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif
public class CenterTextLayer: CATextLayer {
var heightCoefficient: CGFloat = 1
public override init() {
super.init()
}
public override init(layer: Any) {
super.init(layer: layer)
}
public required init(coder aDecoder: NSCoder) {
super.init(layer: aDecoder)
}
public init(coeff: CGFloat) {
super.init()
self.heightCoefficient = coeff
}
public override func draw(in ctx: CGContext) {
#if os(iOS) || os(tvOS)
let multiplier = CGFloat(1)
#elseif os(OSX)
let multiplier = CGFloat(-1)
#endif
let h = ((string as? NSAttributedString)?.size().height ?? fontSize)*heightCoefficient;
let yDiff = (bounds.size.height - h) / 2 * multiplier
ctx.saveGState()
ctx.translateBy(x: 0.0, y: yDiff)
super.draw(in: ctx)
ctx.restoreGState()
}
}

View File

@ -0,0 +1,32 @@
import UIKit
class FlagLayer: CALayer {
let pixelWidth = 1/UIScreen.main.scale
// Flag colors - https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%B0%D0%B3_%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B8
let blue = CGColor(srgbRed: 0, green: 57/256.0, blue: 166/256.0, alpha: 1)
let red = CGColor(srgbRed: 213/256.0, green: 43/256.0, blue: 30/256.0, alpha: 1)
override func draw(in ctx: CGContext) {
print("Draw in bounds: \(bounds)")
ctx.saveGState()
super.draw(in: ctx)
ctx.setStrokeColor(UIColor.black.cgColor)
ctx.setLineWidth(0)
ctx.setFillColor(UIColor.white.cgColor)
ctx.fill(bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: bounds.height*2/3, right: 0)))
ctx.setFillColor(self.blue)
ctx.fill(bounds.insetBy(dx: 0, dy: bounds.height/3))
ctx.setFillColor(self.red)
ctx.fill(bounds.inset(by: UIEdgeInsets(top: bounds.height*2/3, left: 0, bottom: 0, right: 0)))
ctx.setLineWidth(pixelWidth)
ctx.stroke(bounds.insetBy(dx: pixelWidth/2, dy: pixelWidth/2))
ctx.restoreGState()
}
}

View File

@ -1,18 +1,122 @@
import UIKit import UIKit
class PlateView: UIView { class PlateView: UIView {
private var bgLayer = CAShapeLayer() // Some driver plate parameters from "ГОСТ Р 50577-93"
private var mainBgLayer = CAShapeLayer() // http://docs.cntd.ru/document/gost-r-50577-93
private var regionBgLayer = CAShapeLayer() private static let aspectRatio: CGFloat = 112.0/520.0
private static let fontHeightCoeff: CGFloat = 58.0/76.0
var number: String { private var bgLayer = CALayer()
didSet { private var mainBgLayer = CALayer()
private var regionBgLayer = CALayer()
private var numberLayer = CenterTextLayer(coeff: fontHeightCoeff)
private var regionLayer = CenterTextLayer(coeff: fontHeightCoeff)
private var countryLayer = CenterTextLayer()
private var flagLayer = FlagLayer()
} var number: PlateNumber?
} var unrecognized: Bool = false
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
self.number = ""
super.init(coder: coder) super.init(coder: coder)
self.layer.backgroundColor = UIColor.clear.cgColor
self.bgLayer.cornerRadius = 6 //self.bounds.height/8
self.layer.addSublayer(self.bgLayer)
self.mainBgLayer.cornerRadius = 4
self.layer.addSublayer(self.mainBgLayer)
self.regionBgLayer.cornerRadius = 4
self.layer.addSublayer(self.regionBgLayer)
self.numberLayer.alignmentMode = .center
self.numberLayer.contentsScale = UIScreen.main.scale
self.layer.addSublayer(self.numberLayer)
self.regionLayer.alignmentMode = .center
self.regionLayer.contentsScale = UIScreen.main.scale
self.layer.addSublayer(self.regionLayer)
self.countryLayer.contentsScale = UIScreen.main.scale
self.countryLayer.string = "RUS"
self.countryLayer.alignmentMode = .center
self.layer.addSublayer(self.countryLayer)
self.flagLayer.contentsScale = UIScreen.main.scale
self.layer.addSublayer(self.flagLayer)
}
private func pathForFlag(in rect: CGRect) -> CGPath {
let path = CGMutablePath()
let rect = CGPath(rect: rect, transform: nil)
path.addPath(rect)
return path
}
override func layoutSubviews() {
super.layoutSubviews()
guard let number = self.number else { return }
guard let fgColorMain = UIColor(named: "PlateForeground")?.cgColor else { return }
guard let bgColor = UIColor(named: "PlateBackground")?.cgColor else { return }
let fgColor = self.unrecognized ? /*fgColorErr*/UIColor.systemRed.cgColor : fgColorMain
print("Layout for number: \(number.asString())")
self.bgLayer.backgroundColor = fgColor
self.bgLayer.frame = self.bounds
self.mainBgLayer.backgroundColor = bgColor
self.regionBgLayer.backgroundColor = bgColor
self.mainBgLayer.frame = self.bounds.inset(by: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: self.bounds.width*0.27 + 1))
self.regionBgLayer.frame = self.bounds.inset(by: UIEdgeInsets(top: 2, left: self.bounds.width*0.73 + 1, bottom: 2, right: 2))
self.numberLayer.frame = self.mainBgLayer.frame.insetBy(dx: 4, dy: 0)
let font = UIFont(name: "RoadNumbers", size: self.mainBgLayer.frame.height*1.1)!
let attributes: [NSAttributedString.Key: Any] = [
.kern: 3,
.font: font,
.foregroundColor: fgColor
]
let attributed = NSAttributedString(string: number.mainPart(), attributes: attributes)
self.numberLayer.string = attributed
let rbgSize = self.regionBgLayer.frame.size
self.regionLayer.frame = self.regionBgLayer.frame.inset(by: UIEdgeInsets(top: 2, left: 2, bottom: rbgSize.height*0.35, right: 2))
let regionFont = UIFont(name: "RoadNumbers", size: rbgSize.height*0.8)!
let regionAttrs: [NSAttributedString.Key: Any] = [
.kern: 1,
.font: regionFont,
.foregroundColor: fgColor
]
let attributedRegion = NSAttributedString(string: number.region(), attributes: regionAttrs)
self.regionLayer.string = attributedRegion
self.countryLayer.foregroundColor = fgColor
self.countryLayer.frame = self.regionBgLayer.frame.inset(by: UIEdgeInsets(top: rbgSize.height*0.64, left: rbgSize.width*0.08, bottom: rbgSize.height*0.07, right: rbgSize.width*0.42))
self.countryLayer.fontSize = self.countryLayer.frame.size.height
let top = (self.regionBgLayer.frame.origin.y + rbgSize.height*0.68).rounded(.toNearestOrAwayFromZero)
let left = (self.regionBgLayer.frame.origin.x + rbgSize.width*0.62).rounded(.toNearestOrAwayFromZero)
let w = (rbgSize.width*0.34).rounded(.toNearestOrAwayFromZero)
let h = (rbgSize.height*0.28).rounded(.toNearestOrAwayFromZero)
self.flagLayer.frame = CGRect(x: left, y: top, width: w, height: h)
self.flagLayer.setNeedsDisplay()
}
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)
}
} }
} }