Adding/removing/editing events
This commit is contained in:
parent
3332d12b05
commit
734fc5321f
@ -77,6 +77,10 @@
|
||||
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; };
|
||||
7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */; };
|
||||
7A813DC12508C4D900CC93B9 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; };
|
||||
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC22508EE4F00CC93B9 /* EventCell.swift */; };
|
||||
7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */; };
|
||||
7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC8250B5C9700CC93B9 /* LocationRow.swift */; };
|
||||
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */; };
|
||||
7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; };
|
||||
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; };
|
||||
7A96AE2A246AFD6200297C33 /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A96AE29246AFD6200297C33 /* Eureka */; };
|
||||
@ -88,6 +92,7 @@
|
||||
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB562B9249C9E9B00473D53 /* Region.swift */; };
|
||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8D2435D1A000258F61 /* CustomButton.swift */; };
|
||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; };
|
||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
||||
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
|
||||
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
|
||||
@ -160,6 +165,10 @@
|
||||
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>"; };
|
||||
7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||
7A813DC22508EE4F00CC93B9 /* EventCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCell.swift; sourceTree = "<group>"; };
|
||||
7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditController.swift; sourceTree = "<group>"; };
|
||||
7A813DC8250B5C9700CC93B9 /* LocationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRow.swift; sourceTree = "<group>"; };
|
||||
7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerController.swift; sourceTree = "<group>"; };
|
||||
7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = "<group>"; };
|
||||
7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = "<group>"; };
|
||||
7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = "<group>"; };
|
||||
@ -171,6 +180,7 @@
|
||||
7AB562B9249C9E9B00473D53 /* Region.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Region.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>"; };
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Navigation.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>"; };
|
||||
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
||||
@ -248,6 +258,7 @@
|
||||
7A11471423FDEAF800B424AF /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A813DC7250B5C6E00CC93B9 /* Location */,
|
||||
7A11471523FDEB2A00B424AF /* MainSplitController.swift */,
|
||||
7A11471723FDEBFA00B424AF /* ReportController.swift */,
|
||||
7A11471923FE839000B424AF /* AuthController.swift */,
|
||||
@ -259,7 +270,6 @@
|
||||
7A33381024990DAE00D878F1 /* FiltersController.swift */,
|
||||
7A27ADC6249D43210035F39E /* RegionsController.swift */,
|
||||
7A27ADF2249F8B650035F39E /* RecordsController.swift */,
|
||||
7AE26A3424F31B0700625033 /* EventsController.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
@ -329,6 +339,7 @@
|
||||
7A659B5A24A3768A0043A0F2 /* Substrings.swift */,
|
||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */,
|
||||
7A21112924FC3D7E003BBF6F /* AudioEngine.swift */,
|
||||
7ADF6C92250B954900F237B2 /* Navigation.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -354,6 +365,7 @@
|
||||
7A7547DC2403180A004E8406 /* SectionHeader.xib */,
|
||||
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */,
|
||||
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */,
|
||||
7A813DC22508EE4F00CC93B9 /* EventCell.swift */,
|
||||
);
|
||||
path = Cells;
|
||||
sourceTree = "<group>";
|
||||
@ -402,6 +414,17 @@
|
||||
path = Fonts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7A813DC7250B5C6E00CC93B9 /* Location */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */,
|
||||
7AE26A3424F31B0700625033 /* EventsController.swift */,
|
||||
7A813DC8250B5C9700CC93B9 /* LocationRow.swift */,
|
||||
7A813DCA250B5DC900CC93B9 /* LocationPickerController.swift */,
|
||||
);
|
||||
path = Location;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
@ -512,7 +535,9 @@
|
||||
7A64AE822469E16100ABE48E /* IHProgressHUD.swift in Sources */,
|
||||
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
|
||||
7A27ADF924A09CAD0035F39E /* CocoaError.swift in Sources */,
|
||||
7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */,
|
||||
7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */,
|
||||
7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */,
|
||||
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */,
|
||||
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */,
|
||||
7A659B5924A2B1BA0043A0F2 /* AudioRecord.swift in Sources */,
|
||||
@ -549,12 +574,14 @@
|
||||
7A11474723FF2AA500B424AF /* User.swift in Sources */,
|
||||
7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */,
|
||||
7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */,
|
||||
7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */,
|
||||
7A43F9F8246C8A6200BA5B49 /* JWT.swift in Sources */,
|
||||
7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */,
|
||||
7A488C3F24A74B990054D0B2 /* RealmBindObserver.swift in Sources */,
|
||||
7AAE6AD324CDDF950023860B /* VehicleEvent.swift in Sources */,
|
||||
7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */,
|
||||
7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */,
|
||||
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */,
|
||||
7A11474423FF06CA00B424AF /* Api.swift in Sources */,
|
||||
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */,
|
||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||
@ -565,6 +592,7 @@
|
||||
7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */,
|
||||
7A333814249A532400D878F1 /* Filter.swift in Sources */,
|
||||
7A11474B23FF368B00B424AF /* Settings.swift in Sources */,
|
||||
7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */,
|
||||
7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */,
|
||||
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
|
||||
7A64AE812469E16100ABE48E /* ProgressAnimatedView.swift in Sources */,
|
||||
@ -715,7 +743,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
CURRENT_PROJECT_VERSION = 29;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
@ -737,7 +765,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 28;
|
||||
CURRENT_PROJECT_VERSION = 29;
|
||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
|
||||
@ -23,7 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
|
||||
let config = Realm.Configuration(
|
||||
schemaVersion: 15,
|
||||
schemaVersion: 16,
|
||||
migrationBlock: { migration, oldSchemaVersion in
|
||||
if oldSchemaVersion <= 3 {
|
||||
var numbers: [String] = []
|
||||
|
||||
22
AutoCat/Assets.xcassets/MapPin.imageset/Contents.json
vendored
Normal file
22
AutoCat/Assets.xcassets/MapPin.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "map_pin@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "map_pin@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@2x.png
vendored
Normal file
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@3x.png
vendored
Normal file
BIN
AutoCat/Assets.xcassets/MapPin.imageset/map_pin@3x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@ -227,27 +227,94 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="ytQ-Th-luv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<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="28" width="375" height="63"/>
|
||||
<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="63"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HP8-oO-yhP">
|
||||
<rect key="frame" x="8" y="8" width="359" height="47"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcQ-Wz-gJ0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="359" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1tQ-zM-6T9">
|
||||
<rect key="frame" x="0.0" y="28.5" width="359" height="18.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="HP8-oO-yhP" firstAttribute="leading" secondItem="Ypt-ch-fGT" secondAttribute="leading" constant="8" id="BX3-sO-2mI"/>
|
||||
<constraint firstItem="HP8-oO-yhP" firstAttribute="top" secondItem="Ypt-ch-fGT" secondAttribute="top" constant="8" id="kUQ-n4-izs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="HP8-oO-yhP" secondAttribute="trailing" constant="8" id="nGB-VN-Sdx"/>
|
||||
<constraint firstAttribute="bottom" secondItem="HP8-oO-yhP" secondAttribute="bottom" constant="8" id="pFY-Uy-LSv"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="address" destination="xcQ-Wz-gJ0" id="k6e-Yz-MEB"/>
|
||||
<outlet property="date" destination="1tQ-zM-6T9" id="DBs-dL-CmY"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="DmF-8j-ae3" id="ayM-aB-LZb"/>
|
||||
<outlet property="delegate" destination="DmF-8j-ae3" id="O5y-AS-NK0"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="3Ru-VE-szI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
</mapView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="3Ru-VE-szI" secondAttribute="bottom" id="asd-Fg-84K"/>
|
||||
<constraint firstItem="9R5-6n-B3H" firstAttribute="trailing" secondItem="ytQ-Th-luv" secondAttribute="trailing" id="UuJ-8s-LEB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="3Ru-VE-szI" secondAttribute="bottom" id="asd-Fg-84K"/>
|
||||
<constraint firstItem="3Ru-VE-szI" firstAttribute="leading" secondItem="mYm-sh-pSj" secondAttribute="leading" id="bOP-2E-BEk"/>
|
||||
<constraint firstItem="3Ru-VE-szI" firstAttribute="top" secondItem="mYm-sh-pSj" secondAttribute="top" id="cmd-fX-3Qa"/>
|
||||
<constraint firstItem="ytQ-Th-luv" firstAttribute="top" secondItem="9R5-6n-B3H" secondAttribute="top" id="mmu-vo-0BG"/>
|
||||
<constraint firstItem="ytQ-Th-luv" firstAttribute="leading" secondItem="9R5-6n-B3H" secondAttribute="leading" id="pDL-PN-vlh"/>
|
||||
<constraint firstItem="9R5-6n-B3H" firstAttribute="bottom" secondItem="ytQ-Th-luv" secondAttribute="bottom" id="pVh-Q9-Gqi"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3Ru-VE-szI" secondAttribute="trailing" id="w0g-Ng-mkW"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="9R5-6n-B3H"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="map" destination="3Ru-VE-szI" id="rYQ-5P-Kxr"/>
|
||||
<outlet property="tableView" destination="ytQ-Th-luv" id="7Mf-aR-TGJ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="heB-JE-Wi6" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1881" y="1660"/>
|
||||
</scene>
|
||||
<!--Location Edit Controller-->
|
||||
<scene sceneID="Bjx-jy-Fp1">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="LocationEditController" id="HJt-oG-6ic" customClass="LocationEditController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="DJI-63-46l">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="yiT-Qb-YfV"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="xxt-kh-jhI" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2557.5999999999999" y="1658.3208395802101"/>
|
||||
</scene>
|
||||
<!--Search Controller-->
|
||||
<scene sceneID="3Md-yW-a0R">
|
||||
<objects>
|
||||
@ -261,14 +328,14 @@
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="85.5"/>
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="85"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<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.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="85"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<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">
|
||||
<rect key="frame" x="8" y="8" width="124" height="21.5"/>
|
||||
<rect key="frame" x="8" y="8" width="124" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@ -280,7 +347,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cvf-vM-QnT" customClass="PlateView" customModule="AutoCat" customModuleProvider="target">
|
||||
<rect key="frame" x="8" y="37.5" width="317" height="40"/>
|
||||
<rect key="frame" x="8" y="37" width="317" height="40"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="Xoz-Iw-PCU"/>
|
||||
|
||||
26
AutoCat/Cells/EventCell.swift
Normal file
26
AutoCat/Cells/EventCell.swift
Normal file
@ -0,0 +1,26 @@
|
||||
import UIKit
|
||||
|
||||
class EventCell: UITableViewCell {
|
||||
@IBOutlet weak var address: UILabel!
|
||||
@IBOutlet weak var date: UILabel!
|
||||
|
||||
let dateFormatter = DateFormatter()
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.dateFormatter.dateStyle = .short
|
||||
self.dateFormatter.timeStyle = .short
|
||||
}
|
||||
|
||||
func configure(with event: VehicleEvent) {
|
||||
if let addressString = event.address {
|
||||
self.address.text = addressString
|
||||
} else {
|
||||
self.address.text = "Lat: \(event.latitude), Lon: \(event.longitude)"
|
||||
}
|
||||
|
||||
let date = Date(timeIntervalSince1970: event.date)
|
||||
self.date.text = self.dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
import UIKit
|
||||
import MapKit
|
||||
|
||||
class EventPin: NSObject, MKAnnotation {
|
||||
var coordinate: CLLocationCoordinate2D
|
||||
var title: String?
|
||||
var subtitle: String?
|
||||
|
||||
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
|
||||
self.coordinate = coordinate
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
}
|
||||
}
|
||||
|
||||
class EventsController: UIViewController {
|
||||
|
||||
@IBOutlet weak var map: MKMapView!
|
||||
|
||||
public var events: [VehicleEvent] = [] {
|
||||
didSet {
|
||||
self.pins = self.events.map { event in
|
||||
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
||||
let subtitle = event.address ?? "\(event.latitude), \(event.longitude)"
|
||||
let date = Date(timeIntervalSince1970: event.date)
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .short
|
||||
let title = formatter.string(from: date)
|
||||
return EventPin(coordinate: coordinate, title: title, subtitle: subtitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var pins: [EventPin] = []
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
self.map.showsZoomControls = true
|
||||
#endif
|
||||
|
||||
self.map.addAnnotations(self.pins)
|
||||
self.centerMap()
|
||||
}
|
||||
|
||||
func centerMap() {
|
||||
var minLat = 0.0, maxLat = 0.0, minLon = 0.0, maxLon = 0.0
|
||||
for event in self.events {
|
||||
if event.latitude < minLat || minLat == 0 { minLat = event.latitude }
|
||||
if event.latitude > maxLat || maxLat == 0 { maxLat = event.latitude }
|
||||
if event.longitude < minLon || minLon == 0 { minLon = event.longitude }
|
||||
if event.longitude > maxLon || maxLon == 0 { maxLon = event.longitude }
|
||||
}
|
||||
|
||||
let center = CLLocationCoordinate2D(latitude: (minLat + maxLat)/2, longitude: (minLon + maxLon)/2)
|
||||
let leftTop = CLLocation(latitude: minLat, longitude: minLon)
|
||||
let rightBottom = CLLocation(latitude: maxLat, longitude: maxLon)
|
||||
var diagonal = leftTop.distance(from: rightBottom)
|
||||
if diagonal < 1000 {
|
||||
diagonal = 1000
|
||||
}
|
||||
|
||||
let region = MKCoordinateRegion(center: center, latitudinalMeters: diagonal, longitudinalMeters: diagonal)
|
||||
self.map.setRegion(region, animated: true)
|
||||
}
|
||||
}
|
||||
251
AutoCat/Controllers/Location/EventsController.swift
Normal file
251
AutoCat/Controllers/Location/EventsController.swift
Normal file
@ -0,0 +1,251 @@
|
||||
import UIKit
|
||||
import MapKit
|
||||
import RxSwift
|
||||
import Realm
|
||||
import RealmSwift
|
||||
|
||||
class EventPin: NSObject, MKAnnotation {
|
||||
var coordinate: CLLocationCoordinate2D
|
||||
var title: String?
|
||||
var subtitle: String?
|
||||
|
||||
init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String) {
|
||||
self.coordinate = coordinate
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
}
|
||||
}
|
||||
|
||||
enum EventsMode {
|
||||
case map
|
||||
case list
|
||||
}
|
||||
|
||||
class EventsController: UIViewController, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
@IBOutlet weak var map: MKMapView!
|
||||
@IBOutlet weak var tableView: UITableView!
|
||||
|
||||
let bag = DisposeBag()
|
||||
var modeButton: UIBarButtonItem!
|
||||
var addButton: UIBarButtonItem!
|
||||
var mode: EventsMode = .map
|
||||
|
||||
public var vehicle: Vehicle? {
|
||||
didSet {
|
||||
if let vehicle = self.vehicle {
|
||||
self.pins = vehicle.events.map { event in
|
||||
let coordinate = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
||||
let subtitle = event.address ?? "\(event.latitude), \(event.longitude)"
|
||||
let date = Date(timeIntervalSince1970: event.date)
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .short
|
||||
let title = formatter.string(from: date)
|
||||
return EventPin(coordinate: coordinate, title: title, subtitle: subtitle)
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
if self.isViewLoaded {
|
||||
self.updateInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var pins: [EventPin] = []
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.title = self.vehicle?.number ?? "Events"
|
||||
|
||||
#if targetEnvironment(macCatalyst)
|
||||
self.map.showsZoomControls = true
|
||||
#endif
|
||||
|
||||
self.modeButton = UIBarButtonItem(image: UIImage(systemName: "list.bullet"), style: .plain, target: self, action: #selector(switchMode(_:)))
|
||||
self.addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addEvent(_:)))
|
||||
self.navigationItem.rightBarButtonItems = [self.modeButton, self.addButton]
|
||||
|
||||
self.updateInterface()
|
||||
}
|
||||
|
||||
func updateInterface() {
|
||||
self.map.removeAnnotations(self.map.annotations)
|
||||
self.map.addAnnotations(self.pins)
|
||||
self.centerMap()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
func centerMap() {
|
||||
guard let vehicle = self.vehicle else { return }
|
||||
|
||||
var minLat = 0.0, maxLat = 0.0, minLon = 0.0, maxLon = 0.0
|
||||
for event in vehicle.events {
|
||||
if event.latitude < minLat || minLat == 0 { minLat = event.latitude }
|
||||
if event.latitude > maxLat || maxLat == 0 { maxLat = event.latitude }
|
||||
if event.longitude < minLon || minLon == 0 { minLon = event.longitude }
|
||||
if event.longitude > maxLon || maxLon == 0 { maxLon = event.longitude }
|
||||
}
|
||||
|
||||
let center = CLLocationCoordinate2D(latitude: (minLat + maxLat)/2, longitude: (minLon + maxLon)/2)
|
||||
let leftTop = CLLocation(latitude: minLat, longitude: minLon)
|
||||
let rightBottom = CLLocation(latitude: maxLat, longitude: maxLon)
|
||||
var diagonal = leftTop.distance(from: rightBottom)*1.2
|
||||
if diagonal < 1000 {
|
||||
diagonal = 1000
|
||||
}
|
||||
|
||||
let region = MKCoordinateRegion(center: center, latitudinalMeters: diagonal, longitudinalMeters: diagonal)
|
||||
self.map.setRegion(region, animated: true)
|
||||
}
|
||||
|
||||
@objc func switchMode(_ sender: UIBarButtonItem) {
|
||||
switch self.mode {
|
||||
case .map:
|
||||
self.mode = .list
|
||||
self.tableView.reloadData()
|
||||
self.tableView.isHidden = false
|
||||
self.map.isHidden = true
|
||||
self.modeButton.image = UIImage(systemName: "map")
|
||||
case .list:
|
||||
self.mode = .map
|
||||
self.tableView.isHidden = true
|
||||
self.map.isHidden = false
|
||||
self.modeButton.image = UIImage(systemName: "list.bullet")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource
|
||||
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return self.vehicle?.events.count ?? 0
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "EventCell", for: indexPath) as? EventCell else {
|
||||
return UITableViewCell()
|
||||
}
|
||||
|
||||
if let event = self.vehicle?.events[indexPath.row] {
|
||||
cell.configure(with: event)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
|
||||
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
let delete = UIContextualAction(style: .destructive, title: "Delete") { action, view, completion in
|
||||
self.deleteEvent(index: indexPath.row, completion: completion)
|
||||
}
|
||||
delete.image = UIImage(systemName: "trash")
|
||||
|
||||
let edit = UIContextualAction(style: .normal, title: "Edit") { action, view, completion in
|
||||
self.editEvent(index: indexPath.row)
|
||||
completion(true)
|
||||
}
|
||||
edit.image = UIImage(systemName: "pencil")
|
||||
edit.backgroundColor = .systemBlue
|
||||
|
||||
let configuration = UISwipeActionsConfiguration(actions: [delete, edit])
|
||||
configuration.performsFirstActionWithFullSwipe = false
|
||||
return configuration
|
||||
}
|
||||
|
||||
// MARK: - Event actions
|
||||
|
||||
func deleteEvent(index: Int, completion: @escaping (Bool) -> Void) {
|
||||
guard let vehicle = self.vehicle else {
|
||||
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||
return
|
||||
}
|
||||
|
||||
let event = vehicle.events[index]
|
||||
if let eventId = event.id {
|
||||
IHProgressHUD.show()
|
||||
Api.remove(event: eventId).observeOn(MainScheduler.instance).subscribe(onSuccess: { vehicle in
|
||||
completion(self.update(vehicle: vehicle))
|
||||
}, onError: { error in
|
||||
completion(false)
|
||||
IHProgressHUD.show(error: error)
|
||||
print(error)
|
||||
}).disposed(by: self.bag)
|
||||
} else {
|
||||
self.showAlert(title: "Error", message: "Event ID is not found. Please try to update vehicle record, containing this event.")
|
||||
}
|
||||
}
|
||||
|
||||
func editEvent(index: Int) {
|
||||
guard let vehicle = self.vehicle else {
|
||||
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||
return
|
||||
}
|
||||
|
||||
let event = vehicle.events[index]
|
||||
let sb = UIStoryboard(name: "Main", bundle: nil)
|
||||
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
||||
controller.title = "Edit event"
|
||||
controller.date = Date(timeIntervalSince1970: event.date)
|
||||
controller.placemark = Placemark(latitude: event.latitude, longitude: event.longitude, address: event.address)
|
||||
controller.onDone = { newEvent in
|
||||
newEvent.id = event.id
|
||||
self.navigationController?.popViewController(animated: true, completion: {
|
||||
IHProgressHUD.show()
|
||||
Api.edit(event: newEvent)
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
||||
{ error in
|
||||
IHProgressHUD.show(error: error)
|
||||
})
|
||||
.disposed(by: self.bag)
|
||||
})
|
||||
}
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
@objc func addEvent(_ sender: UIBarButtonItem) {
|
||||
guard let vehicle = self.vehicle else {
|
||||
IHProgressHUD.showError(withStatus: "Unknown vehicle")
|
||||
return
|
||||
}
|
||||
|
||||
let sb = UIStoryboard(name: "Main", bundle: nil)
|
||||
let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController
|
||||
controller.title = "Add new event"
|
||||
controller.onDone = { newEvent in
|
||||
self.navigationController?.popViewController(animated: true, completion: {
|
||||
IHProgressHUD.show()
|
||||
Api.add(event: newEvent, to: vehicle.number)
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onSuccess: { self.update(vehicle: $0) }, onError:
|
||||
{ error in
|
||||
IHProgressHUD.show(error: error)
|
||||
})
|
||||
.disposed(by: self.bag)
|
||||
})
|
||||
}
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func update(vehicle: Vehicle) -> Bool {
|
||||
do {
|
||||
if let realm = self.vehicle?.realm {
|
||||
try realm.write {
|
||||
realm.add(vehicle, update: .all)
|
||||
}
|
||||
}
|
||||
self.vehicle = vehicle
|
||||
IHProgressHUD.dismiss()
|
||||
return true
|
||||
} catch {
|
||||
IHProgressHUD.show(error: error)
|
||||
print(error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
55
AutoCat/Controllers/Location/LocationEditController.swift
Normal file
55
AutoCat/Controllers/Location/LocationEditController.swift
Normal file
@ -0,0 +1,55 @@
|
||||
import UIKit
|
||||
import Eureka
|
||||
import RxSwift
|
||||
import CoreLocation
|
||||
|
||||
class LocationEditController: FormViewController {
|
||||
|
||||
private let bag = DisposeBag()
|
||||
private var doneButton: UIBarButtonItem!
|
||||
|
||||
var date = Date()
|
||||
var placemark: Placemark? = nil
|
||||
|
||||
var onDone: ((VehicleEvent) -> Void)?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
self.doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped(_:)))
|
||||
self.navigationItem.rightBarButtonItem = self.doneButton
|
||||
self.doneButton.isEnabled = false
|
||||
|
||||
form +++ Section("")
|
||||
<<< DateTimeInlineRow(){
|
||||
$0.title = "Date and time"
|
||||
$0.value = self.date
|
||||
}.onChange { row in
|
||||
if let newDate = row.value {
|
||||
self.date = newDate
|
||||
}
|
||||
}
|
||||
|
||||
<<< LocationRow() { row in
|
||||
row.title = "Location"
|
||||
row.value = self.placemark
|
||||
}.onChange { row in
|
||||
if let newPlacemark = row.value {
|
||||
self.placemark = newPlacemark
|
||||
self.doneButton.isEnabled = true
|
||||
} else {
|
||||
self.doneButton.isEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func doneTapped(_ sender: UIBarButtonItem) {
|
||||
guard let placemark = self.placemark else { return }
|
||||
let event = VehicleEvent(lat: placemark.latitude, lon: placemark.longitude, speed: -1, dir: -1)
|
||||
event.date = self.date.timeIntervalSince1970
|
||||
if let address = placemark.address {
|
||||
event.address = address
|
||||
}
|
||||
self.onDone?(event)
|
||||
}
|
||||
}
|
||||
156
AutoCat/Controllers/Location/LocationPickerController.swift
Normal file
156
AutoCat/Controllers/Location/LocationPickerController.swift
Normal file
@ -0,0 +1,156 @@
|
||||
import Foundation
|
||||
import MapKit
|
||||
import Eureka
|
||||
import RxSwift
|
||||
import Intents
|
||||
|
||||
public struct Placemark: Equatable {
|
||||
var latitude: Double
|
||||
var longitude: Double
|
||||
var address: String?
|
||||
}
|
||||
|
||||
public class LocationPickerController : UIViewController, TypedRowControllerType, MKMapViewDelegate {
|
||||
|
||||
public var row: RowOf<Placemark>!
|
||||
public var onDismissCallback: ((UIViewController) -> ())?
|
||||
|
||||
private let bag = DisposeBag()
|
||||
private var geocodingDisposable: Disposable?
|
||||
private var address: String?
|
||||
|
||||
lazy var mapView : MKMapView = { [unowned self] in
|
||||
let v = MKMapView(frame: self.view.bounds)
|
||||
v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
return v
|
||||
}()
|
||||
|
||||
lazy var pinView: UIImageView = { [unowned self] in
|
||||
let v = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
|
||||
v.image = UIImage(named: "MapPin", in: Bundle(for: LocationPickerController.self), compatibleWith: nil)
|
||||
v.image = v.image?.withRenderingMode(.alwaysTemplate)
|
||||
v.tintColor = self.view.tintColor
|
||||
v.backgroundColor = .clear
|
||||
v.clipsToBounds = true
|
||||
v.contentMode = .scaleAspectFit
|
||||
v.isUserInteractionEnabled = false
|
||||
return v
|
||||
}()
|
||||
|
||||
let width: CGFloat = 10.0
|
||||
let height: CGFloat = 5.0
|
||||
|
||||
lazy var ellipse: UIBezierPath = { [unowned self] in
|
||||
let ellipse = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: self.width, height: self.height))
|
||||
return ellipse
|
||||
}()
|
||||
|
||||
|
||||
lazy var ellipsisLayer: CAShapeLayer = { [unowned self] in
|
||||
let layer = CAShapeLayer()
|
||||
layer.bounds = CGRect(x: 0, y: 0, width: self.width, height: self.height)
|
||||
layer.path = self.ellipse.cgPath
|
||||
layer.fillColor = UIColor.gray.cgColor
|
||||
layer.fillRule = .nonZero
|
||||
layer.lineCap = .butt
|
||||
layer.lineDashPattern = nil
|
||||
layer.lineDashPhase = 0.0
|
||||
layer.lineJoin = .miter
|
||||
layer.lineWidth = 1.0
|
||||
layer.miterLimit = 10.0
|
||||
layer.strokeColor = UIColor.gray.cgColor
|
||||
return layer
|
||||
}()
|
||||
|
||||
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
convenience public init(_ callback: ((UIViewController) -> ())?){
|
||||
self.init(nibName: nil, bundle: nil)
|
||||
onDismissCallback = callback
|
||||
}
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.addSubview(mapView)
|
||||
|
||||
mapView.delegate = self
|
||||
mapView.addSubview(pinView)
|
||||
mapView.layer.insertSublayer(ellipsisLayer, below: pinView.layer)
|
||||
|
||||
let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(LocationPickerController.tappedDone(_:)))
|
||||
button.title = "Done"
|
||||
navigationItem.rightBarButtonItem = button
|
||||
|
||||
if let value = row.value {
|
||||
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: value.latitude, longitude: value.longitude), latitudinalMeters: 1000, longitudinalMeters: 1000)
|
||||
mapView.setRegion(region, animated: true)
|
||||
}
|
||||
else{
|
||||
mapView.showsUserLocation = true
|
||||
}
|
||||
updateTitle()
|
||||
|
||||
}
|
||||
|
||||
public override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
let center = mapView.convert(mapView.centerCoordinate, toPointTo: pinView)
|
||||
pinView.center = CGPoint(x: center.x, y: center.y - (pinView.bounds.height/2))
|
||||
ellipsisLayer.position = center
|
||||
}
|
||||
|
||||
|
||||
@objc func tappedDone(_ sender: UIBarButtonItem){
|
||||
let target = mapView.convert(ellipsisLayer.position, toCoordinateFrom: mapView)
|
||||
row.value = Placemark(latitude: target.latitude, longitude: target.longitude, address: self.address)
|
||||
onDismissCallback?(self)
|
||||
}
|
||||
|
||||
func updateTitle(){
|
||||
let fmt = NumberFormatter()
|
||||
fmt.maximumFractionDigits = 4
|
||||
fmt.minimumFractionDigits = 4
|
||||
let latitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.latitude))!
|
||||
let longitude = fmt.string(from: NSNumber(value: mapView.centerCoordinate.longitude))!
|
||||
title = "\(latitude), \(longitude)"
|
||||
|
||||
self.address = nil
|
||||
self.geocodingDisposable?.dispose()
|
||||
self.geocodingDisposable = LocationManager
|
||||
.getAddressForLocation(latitude: mapView.centerCoordinate.latitude, longitude: mapView.centerCoordinate.longitude)
|
||||
.observeOn(MainScheduler.instance)
|
||||
.subscribe(onSuccess: { address in
|
||||
self.title = address
|
||||
self.address = address
|
||||
})
|
||||
}
|
||||
|
||||
public func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
|
||||
ellipsisLayer.transform = CATransform3DMakeScale(0.5, 0.5, 1)
|
||||
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
||||
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y - 10)
|
||||
})
|
||||
}
|
||||
|
||||
public func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
|
||||
ellipsisLayer.transform = CATransform3DIdentity
|
||||
UIView.animate(withDuration: 0.2, animations: { [weak self] in
|
||||
self?.pinView.center = CGPoint(x: self!.pinView.center.x, y: self!.pinView.center.y + 10)
|
||||
})
|
||||
updateTitle()
|
||||
}
|
||||
|
||||
public func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
|
||||
mapView.showsUserLocation = false
|
||||
let region = MKCoordinateRegion(center: userLocation.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
|
||||
mapView.setRegion(region, animated: true)
|
||||
}
|
||||
}
|
||||
57
AutoCat/Controllers/Location/LocationRow.swift
Normal file
57
AutoCat/Controllers/Location/LocationRow.swift
Normal file
@ -0,0 +1,57 @@
|
||||
import UIKit
|
||||
import Eureka
|
||||
import CoreLocation
|
||||
|
||||
public final class LocationRow: OptionsRow<PushSelectorCell<Placemark>>, PresenterRowType, RowType {
|
||||
|
||||
public typealias PresenterRow = LocationPickerController
|
||||
|
||||
/// Defines how the view controller will be presented, pushed, etc.
|
||||
public var presentationMode: PresentationMode<PresenterRow>?
|
||||
|
||||
/// Will be called before the presentation occurs.
|
||||
public var onPresentCallback: ((FormViewController, PresenterRow) -> Void)?
|
||||
|
||||
public required init(tag: String?) {
|
||||
super.init(tag: tag)
|
||||
presentationMode = .show(controllerProvider: ControllerProvider.callback { return LocationPickerController(){ _ in } }, onDismiss: { vc in _ = vc.navigationController?.popViewController(animated: true) })
|
||||
|
||||
displayValueFor = {
|
||||
guard let placemark = $0 else { return "" }
|
||||
let fmt = NumberFormatter()
|
||||
fmt.maximumFractionDigits = 4
|
||||
fmt.minimumFractionDigits = 4
|
||||
let latitude = fmt.string(from: NSNumber(value: placemark.latitude))!
|
||||
let longitude = fmt.string(from: NSNumber(value: placemark.longitude))!
|
||||
return "\(latitude), \(longitude)"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Extends `didSelect` method
|
||||
*/
|
||||
public override func customDidSelect() {
|
||||
super.customDidSelect()
|
||||
guard let presentationMode = presentationMode, !isDisabled else { return }
|
||||
if let controller = presentationMode.makeController() {
|
||||
controller.row = self
|
||||
controller.title = selectorTitle ?? controller.title
|
||||
onPresentCallback?(cell.formViewController()!, controller)
|
||||
presentationMode.present(controller, row: self, presentingController: self.cell.formViewController()!)
|
||||
} else {
|
||||
presentationMode.present(nil, row: self, presentingController: self.cell.formViewController()!)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Prepares the pushed row setting its title and completion callback.
|
||||
*/
|
||||
public override func prepare(for segue: UIStoryboardSegue) {
|
||||
super.prepare(for: segue)
|
||||
guard let rowVC = segue.destination as? PresenterRow else { return }
|
||||
rowVC.title = selectorTitle ?? rowVC.title
|
||||
rowVC.onDismissCallback = presentationMode?.onDismissCallback ?? rowVC.onDismissCallback
|
||||
onPresentCallback?(cell.formViewController()!, rowVC)
|
||||
rowVC.row = self
|
||||
}
|
||||
}
|
||||
@ -159,7 +159,6 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
||||
return AudioRecord(path: url.lastPathComponent, number: self.getPlateNumber(from: text), raw: text, duration: duration, event: event)
|
||||
}
|
||||
.subscribe(onSuccess: { record in
|
||||
print(record)
|
||||
let realm = try? Realm()
|
||||
try? realm?.write {
|
||||
realm?.add(record)
|
||||
|
||||
@ -274,8 +274,7 @@ class ReportController: UIViewController, UICollectionViewDataSource, UICollecti
|
||||
}
|
||||
else if let events = self.vehicle?.events, indexPath.row == ReportGeneralSection.events.rawValue && events.count > 0 {
|
||||
let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController
|
||||
controller.events = Array(events)
|
||||
controller.title = self.vehicle?.number ?? "Events"
|
||||
controller.vehicle = self.vehicle
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
32
AutoCat/Extensions/Navigation.swift
Normal file
32
AutoCat/Extensions/Navigation.swift
Normal file
@ -0,0 +1,32 @@
|
||||
import UIKit
|
||||
|
||||
extension UINavigationController {
|
||||
public func pushViewController(
|
||||
_ viewController: UIViewController,
|
||||
animated: Bool,
|
||||
completion: @escaping () -> Void)
|
||||
{
|
||||
pushViewController(viewController, animated: animated)
|
||||
|
||||
guard animated, let coordinator = transitionCoordinator else {
|
||||
DispatchQueue.main.async { completion() }
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||
}
|
||||
|
||||
func popViewController(
|
||||
animated: Bool,
|
||||
completion: @escaping () -> Void)
|
||||
{
|
||||
popViewController(animated: animated)
|
||||
|
||||
guard animated, let coordinator = transitionCoordinator else {
|
||||
DispatchQueue.main.async { completion() }
|
||||
return
|
||||
}
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { _ in completion() }
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
import Foundation
|
||||
import RealmSwift
|
||||
import RxSwift
|
||||
import CoreLocation
|
||||
|
||||
class VehicleEvent: Object, Codable {
|
||||
public class VehicleEvent: Object, Codable {
|
||||
@objc dynamic var id: String?
|
||||
@objc dynamic var date: TimeInterval = Date().timeIntervalSince1970
|
||||
@objc dynamic var latitude: Double = 0
|
||||
@objc dynamic var longitude: Double = 0
|
||||
@ -10,6 +12,10 @@ class VehicleEvent: Object, Codable {
|
||||
@objc dynamic var direction: Double = 0
|
||||
@objc dynamic var address: String? = nil
|
||||
|
||||
var coordinate: CLLocationCoordinate2D {
|
||||
return CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
|
||||
}
|
||||
|
||||
init(lat: Double, lon: Double, speed: Double, dir: Double) {
|
||||
self.latitude = lat
|
||||
self.longitude = lon
|
||||
|
||||
@ -40,14 +40,14 @@ class Api {
|
||||
}
|
||||
|
||||
return URLSession.shared.rx.data(request: request).asSingle().map { data in
|
||||
let str = String(data: data, encoding: .utf8)
|
||||
print("================================")
|
||||
if let string = str?.replacingOccurrences(of: "\\\"", with: "\"")
|
||||
.replacingOccurrences(of: "\\'", with: "'")
|
||||
.replacingOccurrences(of: "\\n", with: "") {
|
||||
print(string)
|
||||
}
|
||||
print("================================")
|
||||
// let str = String(data: data, encoding: .utf8)
|
||||
// print("================================")
|
||||
// if let string = str?.replacingOccurrences(of: "\\\"", with: "\"")
|
||||
// .replacingOccurrences(of: "\\'", with: "'")
|
||||
// .replacingOccurrences(of: "\\n", with: "") {
|
||||
// print(string)
|
||||
// }
|
||||
// print("================================")
|
||||
let resp = try JSONDecoder().decode(Response<T>.self, from: data)
|
||||
if resp.success {
|
||||
return resp.data!
|
||||
@ -231,4 +231,14 @@ class Api {
|
||||
return vehicle
|
||||
}
|
||||
}
|
||||
|
||||
public static func remove(event id: String) -> Single<Vehicle> {
|
||||
let body = ["eventId": id]
|
||||
return self.makeBodyRequest(api: "events", body: body, method: "DELETE")
|
||||
}
|
||||
|
||||
public static func edit(event: VehicleEvent) -> Single<Vehicle> {
|
||||
let body = ["event": event]
|
||||
return self.makeBodyRequest(api: "events", body: body, method: "PUT")
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,12 +106,7 @@ class LocationManager {
|
||||
}
|
||||
|
||||
static func requestCurrentLocation() -> Single<VehicleEvent> {
|
||||
return self.checkPermissions().flatMap(self.requestLocation).do(onSuccess: { event in
|
||||
print("Get location success")
|
||||
}, onSubscribed: {
|
||||
print("Get location subscribed")
|
||||
}, onDispose: {
|
||||
print("Get location dispose")
|
||||
return self.checkPermissions().flatMap(self.requestLocation).do(onDispose: {
|
||||
self.manager.stopUpdatingLocation()
|
||||
})
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ class Recorder {
|
||||
if let transcription = result?.bestTranscription {
|
||||
self.result = transcription.formattedString
|
||||
self.endRecognitionTimer?.invalidate()
|
||||
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { timer in
|
||||
self.endRecognitionTimer = Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { timer in
|
||||
self.finishRecording()
|
||||
observer(.success(self.result))
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user