From 81c7ef119bc69e3226f38c719ccbab0ec1360dc8 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sun, 7 Jun 2020 23:03:02 +0300 Subject: [PATCH] Sharing report --- AutoCat.xcodeproj/project.pbxproj | 12 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 48 ----- .../SteeringWheel.imageset/Contents.json | 16 ++ .../SteeringWheel.imageset/steering_wheel.pdf | Bin 0 -> 4544 bytes AutoCat/Base.lproj/Main.storyboard | 8 +- AutoCat/Cells/VehicleHeaderCell.swift | 5 +- AutoCat/Controllers/ReportController.swift | 79 +++++++- AutoCat/Extensions/ResizeImage.swift | 43 +++++ AutoCat/Extensions/VehicleReportImage.swift | 175 ++++++++++++++++++ AutoCat/Info.plist | 2 + 10 files changed, 329 insertions(+), 59 deletions(-) create mode 100644 AutoCat/Assets.xcassets/SteeringWheel.imageset/Contents.json create mode 100644 AutoCat/Assets.xcassets/SteeringWheel.imageset/steering_wheel.pdf create mode 100644 AutoCat/Extensions/ResizeImage.swift create mode 100644 AutoCat/Extensions/VehicleReportImage.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 38a0c76..af77310 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -58,6 +58,8 @@ 7A7547DD2403180A004E8406 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DB2403180A004E8406 /* SectionHeader.swift */; }; 7A7547DE2403180A004E8406 /* SectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7A7547DC2403180A004E8406 /* SectionHeader.xib */; }; 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.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 */; }; 7A96AE2D246B2B7400297C33 /* GoogleSignInController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */; }; 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; }; @@ -116,6 +118,8 @@ 7A7547DB2403180A004E8406 /* SectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = ""; }; 7A7547DC2403180A004E8406 /* SectionHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SectionHeader.xib; sourceTree = ""; }; 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = ""; }; + 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = ""; }; + 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = ""; }; 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = ""; }; 7A96AE2C246B2B7400297C33 /* GoogleSignInController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GoogleSignInController.swift; sourceTree = ""; }; 7A96AE2E246B2BCD00297C33 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; @@ -258,6 +262,8 @@ isa = PBXGroup; children = ( 7A3F07AA24360DC800E59687 /* Dated.swift */, + 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */, + 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */, ); path = Extensions; sourceTree = ""; @@ -431,6 +437,7 @@ 7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */, 7A6DD90C24335A6D009DE740 /* FlagLayer.swift in Sources */, 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */, + 7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */, 7A6DD90E24337930009DE740 /* PlateNumber.swift in Sources */, 7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */, 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */, @@ -455,6 +462,7 @@ 7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */, 7A11474423FF06CA00B424AF /* Api.swift in Sources */, 7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */, + 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */, 7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */, 7A11474B23FF368B00B424AF /* Settings.swift in Sources */, 7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */, @@ -607,7 +615,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 11; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -629,7 +637,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 10; + CURRENT_PROJECT_VERSION = 11; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index cc526ef..2358118 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -40,53 +40,5 @@ landmarkType = "7"> - - - - - - - - - - - - diff --git a/AutoCat/Assets.xcassets/SteeringWheel.imageset/Contents.json b/AutoCat/Assets.xcassets/SteeringWheel.imageset/Contents.json new file mode 100644 index 0000000..e74fcdb --- /dev/null +++ b/AutoCat/Assets.xcassets/SteeringWheel.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "steering_wheel.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/AutoCat/Assets.xcassets/SteeringWheel.imageset/steering_wheel.pdf b/AutoCat/Assets.xcassets/SteeringWheel.imageset/steering_wheel.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d6fbe948159be8d14b3a6ad6e7abb17947a1362a GIT binary patch literal 4544 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 - + + + + + + + diff --git a/AutoCat/Cells/VehicleHeaderCell.swift b/AutoCat/Cells/VehicleHeaderCell.swift index 77afc4e..d3bf2e6 100644 --- a/AutoCat/Cells/VehicleHeaderCell.swift +++ b/AutoCat/Cells/VehicleHeaderCell.swift @@ -6,15 +6,18 @@ class VehicleHeaderCell: MagazineLayoutCollectionViewCell { @IBOutlet weak var logo: UIImageView! @IBOutlet weak var name: UILabel! + private let placeholder = UIImage(named: "SteeringWheel") + override func prepareForReuse() { self.logo.kf.cancelDownloadTask() } func configure(with vehicle: Vehicle) { self.name.text = vehicle.brand?.name?.original + self.logo.image = self.placeholder if let url = vehicle.brand?.logo { - self.logo.kf.setImage(with: URL(string: url)) + self.logo.kf.setImage(with: URL(string: url), placeholder: self.placeholder) } } } diff --git a/AutoCat/Controllers/ReportController.swift b/AutoCat/Controllers/ReportController.swift index 8cf1910..5f33333 100644 --- a/AutoCat/Controllers/ReportController.swift +++ b/AutoCat/Controllers/ReportController.swift @@ -1,6 +1,7 @@ import UIKit import MagazineLayout import Kingfisher +import LinkPresentation enum ReportSection: Int, CaseIterable, CustomStringConvertible { case header = 0 @@ -74,31 +75,41 @@ enum ReportEngineSection: Int, CaseIterable, CustomStringConvertible { } } -class ReportController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateMagazineLayout, MediaBrowserViewControllerDataSource, MediaBrowserViewControllerDelegate { - +class ReportController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateMagazineLayout, MediaBrowserViewControllerDataSource, MediaBrowserViewControllerDelegate, UIActivityItemSource { + @IBOutlet weak var collection: UICollectionView! private let fullWidth = MagazineLayoutItemSizeMode(widthMode: .fullWidth(respectsHorizontalInsets: true), heightMode: .dynamic) + private var reportImageUrl: URL? var vehicle: Vehicle? { didSet { loadViewIfNeeded() self.collection.reloadData() + self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false) } } + // MARK: - Lifecycle + override func viewDidLoad() { super.viewDidLoad() self.collection.collectionViewLayout = MagazineLayout() let nib = UINib(nibName: "SectionHeader", bundle: nil) self.collection.register(nib, forSupplementaryViewOfKind: MagazineLayout.SupplementaryViewKind.sectionHeader, withReuseIdentifier: "SectionHeader") + + if let vehicle = self.vehicle { + let urls = Array(vehicle.photos.compactMap { URL(string: $0.url) }) + let prefetcher = ImagePrefetcher(urls: urls) + prefetcher.start() + } } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard let ad = UIApplication.shared.delegate as? AppDelegate else { return } - self.navigationController?.setNavigationBarHidden(self.traitCollection.horizontalSizeClass != .compact, animated: false) + self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false) if ad.quickAction == .check { self.dismiss(animated: false, completion: nil) @@ -108,10 +119,6 @@ class ReportController: UIViewController, UICollectionViewDataSource, UICollecti override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - self.navigationController?.setNavigationBarHidden(self.traitCollection.horizontalSizeClass != .compact, animated: false) - } // MARK: - UICollectionViewDataSource @@ -332,4 +339,62 @@ class ReportController: UIViewController, UICollectionViewDataSource, UICollecti guard let photo = self.vehicle?.photos[index] else { return } mediaBrowser.title = photo.description } + + // MARK: - Sharing + + @IBAction func onShare(_ sender: UIBarButtonItem) { + guard let vehicle = self.vehicle else { return } + + let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + sheet.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem + + let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) } + let share = UIAlertAction(title: "Share", style: .default) { _ in + let image = vehicle.reportImage(width: self.collection.contentSize.width) + + do { + let fm = FileManager.default + let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false) + let fileURL = documentDirectory.appendingPathComponent("report.png") + if let imageData = image.pngData() { + try imageData.write(to: fileURL) + self.reportImageUrl = fileURL + } + + let controller = UIActivityViewController(activityItems: [self], applicationActivities: nil) + controller.popoverPresentationController?.barButtonItem = sender + self.present(controller, animated: true) + } catch { + print(error) + } + } + let copyPlateNumber = UIAlertAction(title: "Copy plate number", style: .default) { _ in UIPasteboard.general.string = self.vehicle?.number } + let copyVin = UIAlertAction(title: "Copy VIN", style: .default) { _ in UIPasteboard.general.string = self.vehicle?.vin1 } + + sheet.addAction(share) + sheet.addAction(copyPlateNumber) + sheet.addAction(copyVin) + sheet.addAction(cancel) + self.present(sheet, animated: true, completion: nil) + } + + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { + return UIImage() + } + + func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { + return self.reportImageUrl + } + + func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { + guard let url = self.reportImageUrl else { return nil } + + let metadata = LPLinkMetadata() + metadata.title = self.vehicle?.number + metadata.originalURL = url + metadata.url = url + metadata.imageProvider = NSItemProvider.init(contentsOf: url) + metadata.iconProvider = NSItemProvider.init(contentsOf: url) + return metadata + } } diff --git a/AutoCat/Extensions/ResizeImage.swift b/AutoCat/Extensions/ResizeImage.swift new file mode 100644 index 0000000..045ef05 --- /dev/null +++ b/AutoCat/Extensions/ResizeImage.swift @@ -0,0 +1,43 @@ +import Foundation +import UIKit + +extension UIImage { + class func resize(image: UIImage, targetSize: CGSize) -> UIImage { + let size = image.size + + let widthRatio = targetSize.width / image.size.width + let heightRatio = targetSize.height / image.size.height + + var newSize: CGSize + if widthRatio > heightRatio { + newSize = CGSize(width: size.width * heightRatio, height: size.height * heightRatio) + } else { + newSize = CGSize(width: size.width * widthRatio, height: size.height * widthRatio) + } + + let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) + + UIGraphicsBeginImageContextWithOptions(newSize, false, 0) + image.draw(in: rect) + let newImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return newImage! + } + + class func scale(image: UIImage, by scale: CGFloat) -> UIImage? { + let size = image.size + let scaledSize = CGSize(width: size.width * scale, height: size.height * scale) + return UIImage.resize(image: image, targetSize: scaledSize) + } + + func cutHeight(to newHeight: CGFloat) -> UIImage { + let newSize = CGSize(width: self.size.width, height: newHeight) + let rect = CGRect(origin: .zero, size: newSize) + + let renderer = UIGraphicsImageRenderer(bounds: rect) + return renderer.image { _ in + self.draw(in: CGRect(origin: .zero, size: self.size)) + } + } +} diff --git a/AutoCat/Extensions/VehicleReportImage.swift b/AutoCat/Extensions/VehicleReportImage.swift new file mode 100644 index 0000000..cbbae9a --- /dev/null +++ b/AutoCat/Extensions/VehicleReportImage.swift @@ -0,0 +1,175 @@ +import UIKit +import Kingfisher + +extension Vehicle { + func drawLine(y: CGFloat, width: CGFloat, margin: CGFloat = 15,context: CGContext) { + let lineWidth = 1/UIScreen.main.scale + context.move(to: CGPoint(x: margin, y: y + lineWidth/2)) + context.addLine(to: CGPoint(x: width, y: y + lineWidth/2)) + context.closePath() + context.setLineWidth(lineWidth) + context.setStrokeColor(UIColor.opaqueSeparator.cgColor) + context.strokePath() + } + + func drawCell(y: CGFloat, width: CGFloat, height: CGFloat, title: String, value: String, lineMargin: CGFloat = 15, context: CGContext) { + let fontSize: CGFloat = 17 + let font = UIFont.systemFont(ofSize: fontSize) + let offest = (height - fontSize)/2 + let pStyle = NSMutableParagraphStyle() + pStyle.alignment = .right + let attributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.label] + let valueAttributs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.secondaryLabel, .paragraphStyle: pStyle] + let rect = CGRect(x: 15, y: y + offest, width: width - 30, height: height) + UIColor.secondarySystemGroupedBackground.setFill() + UIRectFill(CGRect(x: 0, y: y, width: width, height: height)) + title.draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) + value.draw(with: rect, options: .usesLineFragmentOrigin, attributes: valueAttributs, context: nil) + self.drawLine(y: y + height - 1/UIScreen.main.scale, width: width, margin: lineMargin, context: context) + } + + func drawBigTextCell(y: CGFloat, width: CGFloat, title: String, value: String, lineMargin: CGFloat = 15, context: CGContext) -> CGFloat { + let pStyle = NSMutableParagraphStyle() + pStyle.alignment = .right + let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.label] + let valueAttributs: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 15), .foregroundColor: UIColor.secondaryLabel, .paragraphStyle: pStyle] + UIColor.secondarySystemGroupedBackground.setFill() + + let text = NSMutableAttributedString(string: title + " - " + value) + text.setAttributes(attributes, range: NSRange(location: 0, length: title.count)) + text.setAttributes(valueAttributs, range: NSRange(location: title.count, length: value.count + 3)) + let textRect = text.boundingRect(with: CGSize(width: width - 30, height: 1000), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil) + let height = textRect.size.height > 28 ? textRect.size.height + 16 : 44 + let offset = (height - textRect.size.height)/2 + let rect = CGRect(x: 15, y: y + offset, width: width - 30, height: height) + UIRectFill(CGRect(x: 0, y: y, width: width, height: height)) + text.draw(with: rect, options: .usesLineFragmentOrigin, context: nil) + self.drawLine(y: y + height - 1/UIScreen.main.scale, width: width, margin: lineMargin, context: context) + + return height + } + + func reportImage(width: CGFloat) -> UIImage { + var realHeight: CGFloat = 0 + let rect = CGRect(origin: .zero, size: CGSize(width: width, height: CGFloat(10000))) + let renderer = UIGraphicsImageRenderer(bounds: rect) + let image = renderer.image { rendererContext in + let cellHeight: CGFloat = 44 + let ctx = rendererContext.cgContext + let centeredStyle = NSMutableParagraphStyle() + centeredStyle.alignment = .center + let titleAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20), .paragraphStyle: centeredStyle, .foregroundColor: UIColor.label] + let headerAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 13), .foregroundColor: UIColor.secondaryLabel] + + let w: CGFloat = width + var y: CGFloat = 0 + + UIColor.systemBackground.setFill() + UIRectFill(rect) + + y += 12 + "\(self.brand?.name?.original ?? "Unknown model") (\(self.number))".draw(with: CGRect(x: 0, y: y, width: w, height: 30), options: .usesLineFragmentOrigin, attributes: titleAttributes, context: nil) + y += 50 + "GENERAL".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) + y += 24 + self.drawLine(y: y, width: w, margin: 0, context: ctx) + y += 1/UIScreen.main.scale + self.drawCell(y: y, width: w, height: cellHeight, title: "Year", value: String(self.year), context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Color", value: self.color ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Category", value: self.category ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Steering wheel position", value: self.isRightWheel ? "Right" : "Left", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Japanese", value: self.isJapanese ? "Yes" : "No", lineMargin: 0, context: ctx) + y += cellHeight + 32 + + "IDENTIFIERS".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) + y += 24 + self.drawLine(y: y, width: w, margin: 0, context: ctx) + y += 1/UIScreen.main.scale + self.drawCell(y: y, width: w, height: cellHeight, title: "Plate number", value: self.number, context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "VIN", value: self.vin1 ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "STS", value: self.sts ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "PTS", value: self.pts ?? "", context: ctx) + y += cellHeight + 32 + + "ENGINE".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) + y += 24 + self.drawLine(y: y, width: w, margin: 0, context: ctx) + y += 1/UIScreen.main.scale + self.drawCell(y: y, width: w, height: cellHeight, title: "Number", value: self.engine?.number ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Fuel type", value: self.engine?.fuelType ?? "", context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Volume", value: String(self.engine?.volume ?? 0), context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Power (HP)", value: String(self.engine?.powerHp ?? 0), context: ctx) + y += cellHeight + self.drawCell(y: y, width: w, height: cellHeight, title: "Power (kw)", value: String(self.engine?.powerKw ?? 0), context: ctx) + y += cellHeight + 32 + + "OWNERSHIP PERIODS (\(self.ownershipPeriods.count))".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) + y += 24 + self.drawLine(y: y, width: w, margin: 0, context: ctx) + y += 1/UIScreen.main.scale + + let formatter = DateFormatter() + formatter.dateStyle = .long + formatter.timeStyle = .none + for period in self.ownershipPeriods { + self.drawCell(y: y, width: w, height: cellHeight, title: "Owner type", value: period.ownerType, context: ctx) + y += cellHeight + + let fromDate = Date(timeIntervalSince1970: TimeInterval(period.from/1000)) + self.drawCell(y: y, width: w, height: cellHeight, title: "From", value: formatter.string(from: fromDate), context: ctx) + y += cellHeight + + let toDate = Date(timeIntervalSince1970: TimeInterval(period.to/1000)) + let toString = period.to == 0 ? "now" : formatter.string(from: toDate) + self.drawCell(y: y, width: w, height: cellHeight, title: "To", value: toString, context: ctx) + y += cellHeight + + let height = self.drawBigTextCell(y: y, width: w, title: "Last operation", value: period.lastOperation, lineMargin: 0, context: ctx) + y += height + 8 + } + + y += 24 + + "PHOTOS (\(self.photos.count))".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) + y += 24 + self.drawLine(y: y, width: w, margin: 0, context: ctx) + y += 1/UIScreen.main.scale + + for photo in self.photos { + let date = Date(timeIntervalSince1970: TimeInterval(photo.date/1000)) + var name = "" + if let brand = photo.brand, let model = photo.model { + name = "\(brand) \(model)" + } + + if let url = URL(string: photo.url) { + if let image = ImageCache.default.retrieveImageInDiskCache(forKey: url.cacheKey) { + let imgHeight = image.size.height*w/image.size.width + let rect = CGRect(x: 0, y: y, width: w, height: imgHeight) + image.draw(in: rect) + y += imgHeight + } + } + + self.drawCell(y: y, width: w, height: cellHeight, title: name, value: formatter.string(from: date), lineMargin: 0, context: ctx) + y += cellHeight + + y += 16 + } + + realHeight = y + } + + return image.cutHeight(to: realHeight) + } +} diff --git a/AutoCat/Info.plist b/AutoCat/Info.plist index afefe6a..b56f5ca 100644 --- a/AutoCat/Info.plist +++ b/AutoCat/Info.plist @@ -42,6 +42,8 @@ + NSPhotoLibraryAddUsageDescription + AutoCat needs access to photo library to save reports UIAppFonts RoadNumbers2.0.otf