From e7587001d69502e1f8e381cb4a2a0685ad7e7fd5 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sat, 17 Oct 2020 15:13:10 -0500 Subject: [PATCH 01/20] Create UI for ProductAttributes in Detail View The product summary Overview needs to display rows for a product's Attributes, whose data are fetched from the newly integrated ProductAttribute API endpoint. This required a subclass of UIView and a new class of TableView Cells to hold them. When the user taps an Attribute row, a pop-up info card will be displayed showing a longer description. Swiping left will show more related information on the pop-up info card. Resolves: #707 --- Sources/Models/Common/Form.swift | 3 +- .../Detail/ProductDetailViewController.swift | 19 ++- .../AttributeTableViewCell.swift | 131 ++++++++++++++++ .../AttributeTableViewCell.xib | 50 ++++++ .../ProductAttributes/AttributeView.swift | 144 ++++++++++++++++++ .../ProductAttributes/AttributeView.xib | 57 +++++++ 6 files changed, 401 insertions(+), 3 deletions(-) create mode 100644 Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift create mode 100644 Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.xib create mode 100644 Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift create mode 100644 Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib diff --git a/Sources/Models/Common/Form.swift b/Sources/Models/Common/Form.swift index 6233c7bab53..5b255ea6b7c 100644 --- a/Sources/Models/Common/Form.swift +++ b/Sources/Models/Common/Form.swift @@ -23,7 +23,8 @@ struct Form { ProductDetailWebViewTableViewCell.self, SummaryHeaderCell.self, SummaryFooterCell.self, - HostedViewCell.self + HostedViewCell.self, + AttributeTableViewCell.self ] } } diff --git a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift index 6f9584e7a59..fb6fece81f9 100644 --- a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift @@ -61,8 +61,6 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - //TODO: Answers.logContentView(withName: "Product's detail", contentType: "product_detail", contentId: product.barcode, customAttributes: ["product_name": product.name ?? ""]) - if let parentVc = parent as? UINavigationController { parentVc.navigationBar.isTranslucent = false @@ -511,6 +509,23 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan } } + private func createProductAttributeRows(rows: inout [FormRow]) { + if let attributeGroups = product.productAttributes?.attributeGroups { + for attrGroup in attributeGroups where attrGroup.id == "labels" { + if let attributes = attrGroup.attributes { + for attribute in attributes { + if let desc = attribute.descriptionShort ?? attribute.title, + desc != "", let name = attribute.name, name != "" { + createFormRow(with: &rows, item: attribute, label: attribute.name, cellType: AttributeTableViewCell.self) + } else { + continue + } + } + } + } + } + } + private func createFormRow(with array: inout [FormRow], item: Any?, label: String? = nil, cellType: ProductDetailBaseCell.Type = InfoRowTableViewCell.self, isCopiable: Bool = false, separator: String = ", ") { // Check item has a value, if so add to the array of rows. switch item { diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift new file mode 100644 index 00000000000..b934e087e11 --- /dev/null +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift @@ -0,0 +1,131 @@ +// +// AttributeTableViewCell.swift +// OpenFoodFacts +// +// Created by Alexander Scott Beaty on on 17/10/2020. +// Copyright © 2020 Alexander Scott Beaty. All rights reserved. +// + +import UIKit +import BLTNBoard +import Kingfisher +import Cartography + +class AttributeTableViewCell: ProductDetailBaseCell { + + @IBOutlet weak var stackView: UIStackView! + + var viewController: FormTableViewController? + var attribute: Attribute? + + fileprivate var gestureRecognizer: UITapGestureRecognizer? + var bulletinManager: BLTNItemManager! + + override func configure(with formRow: FormRow, in viewController: FormTableViewController) { + guard let attribute = formRow.value as? Attribute else { return } + self.attribute = attribute + self.viewController = viewController + + removeGestureRecognizer() + // 'circle "i"' infoImage is in xib, need to retrieve it and add it back later + let infoImageView = stackView.arrangedSubviews.last + stackView.removeAllViews() + + let attributeView = AttributeView.loadFromNib() + attributeView.configure(attribute) + + configureGestureRecognizer() + stackView.addArrangedSubview(attributeView) + if let iiv = infoImageView { + stackView.addArrangedSubview(iiv) + } + } + + override func dismiss() { + super.dismiss() + stackView.arrangedSubviews.forEach { + if let view = $0 as? IngredientsAnalysisView { + view.removeGestureRecognizer() + } + } + stackView.removeAllViews() + } + + func configureGestureRecognizer() { + self.gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTap)) + if let gestureRecognizer = self.gestureRecognizer { + self.addGestureRecognizer(gestureRecognizer) + self.isUserInteractionEnabled = true + } + } + + func removeGestureRecognizer() { + if let validGesture = self.gestureRecognizer { + self.removeGestureRecognizer(validGesture) + self.gestureRecognizer = nil + } + } + + @objc func didTap(_ sender: UITapGestureRecognizer) { + print("Attribute tapped") + guard let attribute = attribute else { + return + } + let page = AttributeBLTNPageItem() + page.isDismissable = true + page.requiresCloseButton = false + + let attributeView = AttributeView.loadFromNib() + attributeView.configure(attribute) + page.attributeView = attributeView + page.attribute = attribute + + page.iconImageBackgroundColor = self.backgroundColor + page.alternativeButtonTitle = "generic.ok".localized + page.alternativeHandler = { item in + item.manager?.dismissBulletin() + } + + bulletinManager = BLTNItemManager(rootItem: page) + bulletinManager.showBulletin(in: UIApplication.shared) + + page.alternativeButton?.titleLabel?.numberOfLines = 2 + page.alternativeButton?.titleLabel?.textAlignment = .center + } +} + +class AttributeBLTNPageItem: BLTNPageItem { + var attribute: Attribute? + var iconImageBackgroundColor: UIColor? + var attributeView: AttributeView? + + override func makeHeaderViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView]? { + let containerA = UIView() + containerA.backgroundColor = iconImageBackgroundColor + + if let attributeV = attributeView { + containerA.addSubview(attributeV) + + constrain(attributeV, containerA) { (attributeV, containerA) in + containerA.width == attributeV.width + containerA.height == attributeV.height + attributeV.edges == containerA.edges + + } + + var views: [UIView] = [containerA] + + if let attribute = attribute { + let descriptionLabel = UILabel() + descriptionLabel.numberOfLines = 0 + descriptionLabel.textAlignment = .center + descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) + + descriptionLabel.text = attribute.descriptionLong ?? attribute.descriptionShort ?? "empty description" + views.append(descriptionLabel) + } + return views + } + return nil + } +} diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.xib new file mode 100644 index 00000000000..5bf48e48048 --- /dev/null +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.xib @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift new file mode 100644 index 00000000000..9be934a6c83 --- /dev/null +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -0,0 +1,144 @@ +// +// AttributeView.swift +// OpenFoodFacts +// +// Created by Alexander Scott Beaty on on 17/10/2020. +// Copyright © 2020 Alexander Scott Beaty. All rights reserved. +// + +import UIKit +import BLTNBoard +import Kingfisher +import Cartography + +@IBDesignable class AttributeView: UIView { + + @IBOutlet weak var iconImageView: UIImageView! + @IBOutlet weak var descriptionShort: UITextView! + + var attribute: Attribute? + + var openProductEditHandler: (() -> Void)? + + func configure(_ attribute: Attribute) { + self.layer.cornerRadius = 5 + self.attribute = attribute + setIconImageView(imageURL: attribute.iconUrl) + if let label = attribute.name, let description = attribute.descriptionShort ?? attribute.title { + let text = AttributedStringFormatter.formatAttributedText(label: label, description: description) + descriptionShort.attributedText = text + } + } + + static func loadFromNib() -> AttributeView { + let nib = UINib(nibName: "AttributeView", bundle: Bundle.main) + // swiftlint:disable:next force_cast + let view = nib.instantiate(withOwner: self, options: nil).first as! AttributeView + return view + } + + func setIconImageView(imageURL: String?) { + guard let icon = imageURL, + let url = URL(string: icon) + else { + iconImageView.isHidden = false + return + } + iconImageView.kf.indicatorType = .activity + iconImageView.kf.setImage(with: url) + iconImageView.isHidden = false + } + + var bulletinManager: BLTNItemManager! + + deinit { + if bulletinManager != nil { + if bulletinManager.isShowingBulletin { + bulletinManager.dismissBulletin(animated: true) + } + bulletinManager = nil + } + } +} + +protocol formatAttributedString { + static var boldWordsPattern: String {get} + + static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? + static func makeWordsBold(for originalText: NSAttributedString) -> NSAttributedString +} + +class AttributedStringFormatter: formatAttributedString { + static var boldWordsPattern: String { return "(_\\w+_)" } + + static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? { + let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.caption1).pointSize) + var bold: [NSAttributedString.Key: Any] = [:] + if #available(iOS 13.0, *) { + bold = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: headline] + } else { + bold = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font: headline] + } + + let body = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.caption2).pointSize) + var regular: [NSAttributedString.Key: Any] = [:] + if #available(iOS 13.0, *) { + regular = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: body] + } else { + regular = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font: body] + } + let combination = NSMutableAttributedString() + combination.append(NSAttributedString(string: label + "\n", attributes: bold)) + + let descrip = NSAttributedString(string: description, attributes: regular) + combination.append(descrip) + + return combination + } + + /// Create an attributed string with word surrounded by '_' (e.g. _Milk_) bold. + /// + /// - Parameter originalText: Original text with words to be made bold surrounded by '_' + /// - Returns: NSAttributedString with highlighted words + static func makeWordsBold(for originalText: NSAttributedString) -> NSAttributedString { + let body = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body).pointSize) + let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline).pointSize) + let highlightedText = NSMutableAttributedString(attributedString: originalText) + var bold: [NSAttributedString.Key: Any] = [:] + if #available(iOS 13.0, *) { + bold = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: headline] + } else { + bold = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font: headline] + } + var regular: [NSAttributedString.Key: Any] = [:] + if #available(iOS 13.0, *) { + regular = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: body] + } else { + regular = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font: body] + } + highlightedText.addAttributes(regular, range: originalText.string.nsrange) + + do { + let regex = try NSRegularExpression(pattern: boldWordsPattern) + let matches = regex.matches(in: originalText.string, range: originalText.string.nsrange) + + for match in matches.reversed() { + highlightedText.setAttributes(bold, range: match.range) + + // Delete underscore characters + var trailingRange = match.range(at: 1) + trailingRange.location += trailingRange.length - 1 + trailingRange.length = 1 + var initialRange = match.range(at: 1) + initialRange.length = 1 + highlightedText.deleteCharacters(in: trailingRange) + highlightedText.deleteCharacters(in: initialRange) + } + } catch let error { + let userInfo = ["bold_words_pattern": boldWordsPattern, "original_text": originalText.string] + AnalyticsManager.record(error: error, withAdditionalUserInfo: userInfo) + } + + return highlightedText + } +} diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib new file mode 100644 index 00000000000..69ee1556323 --- /dev/null +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c4efdf9ed461204e8d1401fd7a33e600048c586c Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sun, 1 Nov 2020 16:27:36 -0600 Subject: [PATCH 02/20] Display AttributeCells and increase attributed string font size --- .../Products/Detail/ProductDetailViewController.swift | 2 ++ .../Products/Detail/ProductAttributes/AttributeView.swift | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift index fb6fece81f9..ce406685e9c 100644 --- a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift @@ -240,6 +240,8 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan return NSAttributedString(string: tag) }), label: InfoRowKey.countries.localizedString) + createProductAttributeRows(rows: &rows) + // Footer rows.append(FormRow(value: product as Any, cellType: SummaryFooterCell.self)) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 9be934a6c83..af9fc45ead4 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -72,7 +72,7 @@ class AttributedStringFormatter: formatAttributedString { static var boldWordsPattern: String { return "(_\\w+_)" } static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? { - let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.caption1).pointSize) + let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body).pointSize) var bold: [NSAttributedString.Key: Any] = [:] if #available(iOS 13.0, *) { bold = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: headline] @@ -80,7 +80,7 @@ class AttributedStringFormatter: formatAttributedString { bold = [NSAttributedString.Key.foregroundColor: UIColor.black, NSAttributedString.Key.font: headline] } - let body = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.caption2).pointSize) + let body = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body).pointSize) var regular: [NSAttributedString.Key: Any] = [:] if #available(iOS 13.0, *) { regular = [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: body] From 2f8f9eb554895b16f0478af7c1813927664b6d56 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Fri, 6 Nov 2020 19:48:35 -0600 Subject: [PATCH 03/20] Attempt to show floatingPanel from closure This snapshot is not workable, I just want to preserve an idea which probably won't work, which is passing a closure to AttributeTableViewCell to launch the floatingPanel. However, I made FormTableViewController the owner of the floating panel and that is not allowed. So now I am switching to a new strategy to make a delegate for AttributeTableViewCell and designate the ProductDetailTableViewController as the delegate and as the floating panel host as I originally intended. --- .../ProductAttributeViewController.swift | 12 +++ ...ProductDetailViewControllerExtension.swift | 94 +++++++++++++++++++ .../Detail/ProductDetailViewController.swift | 6 ++ .../AttributeTableViewCell.swift | 32 +++---- .../ProductAttributes/AttributeView.swift | 2 - 5 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift create mode 100644 Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift new file mode 100644 index 00000000000..d549ab98424 --- /dev/null +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -0,0 +1,12 @@ +// +// ProductAttributeViewController.swift +// OpenFoodFacts +// +// Created by Alexander Scott Beaty on 11/3/20. +// + +import UIKit + +class ProductAttributeViewController: UIViewController { + +} diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift new file mode 100644 index 00000000000..a4c1bd2e072 --- /dev/null +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -0,0 +1,94 @@ +// +// ProductDetailViewControllerExtension.swift +// OpenFoodFacts +// +// Created by Alexander Scott Beaty on 11/3/20. +// +import FloatingPanel + +extension ProductDetailViewController { + // MARK: - ProductAttributes FloatingPanel setup + func configureFloatingPanel(_ attributeView: AttributeView) { + + floatingPanelController = FloatingPanelController() + floatingPanelController.delegate = self + floatingPanelController.contentMode = .fitToBounds + + // Add the floating panel view to the controller's view on top of other views. + self.view.addSubview(floatingPanelController.view) + floatingPanelController.view.frame = self.view.bounds + + // In addition, Auto Layout constraints are highly recommended. + // Constraint the fpc.view to all four edges of your controller's view. + // It makes the layout more robust on trait collection change. + floatingPanelController.view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + floatingPanelController.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0.0), + floatingPanelController.view.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0.0), + floatingPanelController.view.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0.0), + floatingPanelController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0) + ]) + + productAttributeController = ProductAttributeViewController() + productAttributeController.view.addSubview(attributeView) + floatingPanelController.set(contentViewController: productAttributeController) + + floatingPanelController.surfaceView.backgroundColor = .clear + floatingPanelController.surfaceView.cornerRadius = 9.0 + floatingPanelController.surfaceView.shadowHidden = false + // Add a gesture to hide the summaryView + let gestureDown = UISwipeGestureRecognizer(target: self, action: #selector(self.hideSummaryView(_:))) + gestureDown.numberOfTouchesRequired = 1 + gestureDown.direction = .down + floatingPanelController.surfaceView.addGestureRecognizer(gestureDown) + floatingPanelController.surfaceView.isUserInteractionEnabled = true + + floatingPanelController.addPanel(toParent: self) + } + + // MARK: - Gesture recognizers + @objc func hideSummaryView(_ sender: UISwipeGestureRecognizer) { + floatingPanelController.move(to: FloatingPanelPosition.hidden, animated: true) + } + +} +extension FormTableViewController: FloatingPanelControllerDelegate { + + func floatingPanel(_ viewController: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { + return productAttributeFloatingPanelLayout + } + + func floatingPanelDidChangePosition(_ floatingPanelVC: FloatingPanelController) { + if floatingPanelVC.position != .full { + self.view.endEditing(true) + } + } + +} + +class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { + + fileprivate var canShowDetails: Bool = false + + public var initialPosition: FloatingPanelPosition { + return .hidden + } + + public var supportedPositions: Set { + return canShowDetails ? [.full, .tip] : [.tip] + } + + public func insetFor(position: FloatingPanelPosition) -> CGFloat? { + switch position { + case .full: return 16.0 + case .tip: return 112.0 + 16.0 + default: return nil + } + } +} + +extension ProductDetailViewController: AttributeTableViewCellDelegate { + func attributeTableViewCellDelegate(_ sender: AttributeTableViewCell, receivedTapOn view: UIView) { + <#code#> + } +} diff --git a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift index ce406685e9c..1f967f6ae05 100644 --- a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift @@ -8,6 +8,7 @@ import UIKit import XLPagerTabStrip +import FloatingPanel class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataManagerClient { @@ -17,6 +18,11 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan var dataManager: DataManagerProtocol! private var notificationCentertoken: NotificationCenterToken? + // for Product Attribute detail display + var floatingPanelController: FloatingPanelController! + var productAttributeFloatingPanelLayout = ProductAttributeFloatingPanelLayout() + var productAttributeController: ProductAttributeViewController! + override func viewDidLoad() { super.viewDidLoad() diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift index b934e087e11..9d08583f91e 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift @@ -11,12 +11,17 @@ import BLTNBoard import Kingfisher import Cartography +protocol AttributeTableViewCellDelegate: class { + func attributeTableViewCellDelegate(_ sender: AttributeTableViewCell, receivedTapOn view: UIView) +} + class AttributeTableViewCell: ProductDetailBaseCell { @IBOutlet weak var stackView: UIStackView! - var viewController: FormTableViewController? + var attribute: Attribute? + var showFloatingPanelHandler: ((AttributeView) -> Void)? fileprivate var gestureRecognizer: UITapGestureRecognizer? var bulletinManager: BLTNItemManager! @@ -24,7 +29,6 @@ class AttributeTableViewCell: ProductDetailBaseCell { override func configure(with formRow: FormRow, in viewController: FormTableViewController) { guard let attribute = formRow.value as? Attribute else { return } self.attribute = attribute - self.viewController = viewController removeGestureRecognizer() // 'circle "i"' infoImage is in xib, need to retrieve it and add it back later @@ -39,6 +43,13 @@ class AttributeTableViewCell: ProductDetailBaseCell { if let iiv = infoImageView { stackView.addArrangedSubview(iiv) } + + // configure floating panel for the ProductAttribute rows + formViewController?.configureFloatingPanel(attributeView) + formViewController?.floatingPanelController.move(to: .hidden, animated: false) + showFloatingPanelHandler = { [weak self] attributeView in + self?.formViewController?.floatingPanelController.move(to: .full, animated: true) + } } override func dismiss() { @@ -71,26 +82,11 @@ class AttributeTableViewCell: ProductDetailBaseCell { guard let attribute = attribute else { return } - let page = AttributeBLTNPageItem() - page.isDismissable = true - page.requiresCloseButton = false let attributeView = AttributeView.loadFromNib() attributeView.configure(attribute) - page.attributeView = attributeView - page.attribute = attribute - - page.iconImageBackgroundColor = self.backgroundColor - page.alternativeButtonTitle = "generic.ok".localized - page.alternativeHandler = { item in - item.manager?.dismissBulletin() - } - - bulletinManager = BLTNItemManager(rootItem: page) - bulletinManager.showBulletin(in: UIApplication.shared) - page.alternativeButton?.titleLabel?.numberOfLines = 2 - page.alternativeButton?.titleLabel?.textAlignment = .center + showFloatingPanelHandler?(attributeView) } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index af9fc45ead4..0f2ee89239d 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -18,8 +18,6 @@ import Cartography var attribute: Attribute? - var openProductEditHandler: (() -> Void)? - func configure(_ attribute: Attribute) { self.layer.cornerRadius = 5 self.attribute = attribute From f49d47ffc1f727de4c340f5fc6726fdbe72cecc2 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sat, 7 Nov 2020 22:30:28 -0600 Subject: [PATCH 04/20] Create custom VC for ProdAttr. and use delegation Created delegate protocol for AttributeTableViewCell to call back to ProductDetailViewController so it can launch the FloatingPanelVC Extend ProductDetailVC so it can handle FloatingPanels similar to ScannerViewController. Create ProductAttributeViewController.swift and .storyboard to be the FloatingPanels VC and view to hold AttributeView Resolves: #707 --- Sources/Models/Common/AttributeTableRow.swift | 18 ++++++++ .../ProductAttributeViewController.swift | 1 + ...ProductDetailViewControllerExtension.swift | 14 +++--- .../Detail/ProductDetailViewController.swift | 9 +++- .../AttributeTableViewCell.swift | 20 ++++----- .../ProductAttributeViewController.storyboard | 44 +++++++++++++++++++ 6 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 Sources/Models/Common/AttributeTableRow.swift create mode 100644 Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard diff --git a/Sources/Models/Common/AttributeTableRow.swift b/Sources/Models/Common/AttributeTableRow.swift new file mode 100644 index 00000000000..b2f619ff882 --- /dev/null +++ b/Sources/Models/Common/AttributeTableRow.swift @@ -0,0 +1,18 @@ +// +// AttributeTableRow.swift +// OpenFoodFacts +// +// Created by Alexander Scott Beaty on 11/6/20. +// + +import Foundation + +struct AttributeTableRow { + weak var delegate: AttributeTableViewCellDelegate? + let attribute: Attribute? + + init(_ delegate: AttributeTableViewCellDelegate?, attribute: Attribute?) { + self.delegate = delegate + self.attribute = attribute + } +} diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index d549ab98424..caa9a8d9764 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -8,5 +8,6 @@ import UIKit class ProductAttributeViewController: UIViewController { + @IBOutlet var stackView: UIStackView! } diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index a4c1bd2e072..7ac26156963 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -8,7 +8,7 @@ import FloatingPanel extension ProductDetailViewController { // MARK: - ProductAttributes FloatingPanel setup - func configureFloatingPanel(_ attributeView: AttributeView) { + func configureFloatingPanel() { floatingPanelController = FloatingPanelController() floatingPanelController.delegate = self @@ -29,14 +29,13 @@ extension ProductDetailViewController { floatingPanelController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0) ]) - productAttributeController = ProductAttributeViewController() - productAttributeController.view.addSubview(attributeView) + productAttributeController = ProductAttributeViewController.loadFromStoryboard() as ProductAttributeViewController floatingPanelController.set(contentViewController: productAttributeController) floatingPanelController.surfaceView.backgroundColor = .clear floatingPanelController.surfaceView.cornerRadius = 9.0 floatingPanelController.surfaceView.shadowHidden = false - // Add a gesture to hide the summaryView + // Add a gesture to hide the floating panel let gestureDown = UISwipeGestureRecognizer(target: self, action: #selector(self.hideSummaryView(_:))) gestureDown.numberOfTouchesRequired = 1 gestureDown.direction = .down @@ -52,7 +51,7 @@ extension ProductDetailViewController { } } -extension FormTableViewController: FloatingPanelControllerDelegate { +extension ProductDetailViewController: FloatingPanelControllerDelegate { func floatingPanel(_ viewController: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { return productAttributeFloatingPanelLayout @@ -88,7 +87,8 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { } extension ProductDetailViewController: AttributeTableViewCellDelegate { - func attributeTableViewCellDelegate(_ sender: AttributeTableViewCell, receivedTapOn view: UIView) { - <#code#> + func attributeTableViewCellTapped(_ sender: AttributeTableViewCell, _ attributeView: AttributeView) { + productAttributeController.stackView.addArrangedSubview(attributeView) + floatingPanelController.move(to: FloatingPanelPosition.full, animated: true) } } diff --git a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift index 1f967f6ae05..f37b6056b4a 100644 --- a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift @@ -48,6 +48,10 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan } } } + + // set up floatingPanel + configureFloatingPanel() + setUserAgent() notificationCentertoken = NotificationCenter.default.observe( @@ -78,6 +82,9 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan } self.refreshLatestRobotoffQuestion() + + // floatingPanel + self.floatingPanelController.move(to: .hidden, animated: false) } override func viewWillDisappear(_ animated: Bool) { @@ -524,7 +531,7 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan for attribute in attributes { if let desc = attribute.descriptionShort ?? attribute.title, desc != "", let name = attribute.name, name != "" { - createFormRow(with: &rows, item: attribute, label: attribute.name, cellType: AttributeTableViewCell.self) + createFormRow(with: &rows, item: AttributeTableRow(self, attribute: attribute), label: attribute.name, cellType: AttributeTableViewCell.self) } else { continue } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift index 9d08583f91e..1daeeb190b3 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift @@ -12,22 +12,24 @@ import Kingfisher import Cartography protocol AttributeTableViewCellDelegate: class { - func attributeTableViewCellDelegate(_ sender: AttributeTableViewCell, receivedTapOn view: UIView) + func attributeTableViewCellTapped(_ sender: AttributeTableViewCell, _ attributeView: AttributeView) } class AttributeTableViewCell: ProductDetailBaseCell { @IBOutlet weak var stackView: UIStackView! - + public weak var delegate: AttributeTableViewCellDelegate? var attribute: Attribute? - var showFloatingPanelHandler: ((AttributeView) -> Void)? fileprivate var gestureRecognizer: UITapGestureRecognizer? var bulletinManager: BLTNItemManager! override func configure(with formRow: FormRow, in viewController: FormTableViewController) { - guard let attribute = formRow.value as? Attribute else { return } + guard let attributeTableRow = formRow.value as? AttributeTableRow, + let attribute = attributeTableRow.attribute + else { return } + self.attribute = attribute removeGestureRecognizer() @@ -36,6 +38,7 @@ class AttributeTableViewCell: ProductDetailBaseCell { stackView.removeAllViews() let attributeView = AttributeView.loadFromNib() + attributeView.configure(attribute) configureGestureRecognizer() @@ -44,12 +47,7 @@ class AttributeTableViewCell: ProductDetailBaseCell { stackView.addArrangedSubview(iiv) } - // configure floating panel for the ProductAttribute rows - formViewController?.configureFloatingPanel(attributeView) - formViewController?.floatingPanelController.move(to: .hidden, animated: false) - showFloatingPanelHandler = { [weak self] attributeView in - self?.formViewController?.floatingPanelController.move(to: .full, animated: true) - } + delegate = attributeTableRow.delegate } override func dismiss() { @@ -86,7 +84,7 @@ class AttributeTableViewCell: ProductDetailBaseCell { let attributeView = AttributeView.loadFromNib() attributeView.configure(attribute) - showFloatingPanelHandler?(attributeView) + delegate?.attributeTableViewCellTapped(self, attributeView) } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard b/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard new file mode 100644 index 00000000000..1f34dc83efa --- /dev/null +++ b/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 788eeb1e560eb62e2cabaa0cb30c167c865c4929 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sun, 8 Nov 2020 13:10:23 -0600 Subject: [PATCH 05/20] Display floating panel --- .../ProductAttributeViewController.swift | 24 +++++++++++++++++-- ...ProductDetailViewControllerExtension.swift | 9 +++++-- .../ProductAttributeViewController.storyboard | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index caa9a8d9764..f6c36a29e68 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -8,6 +8,26 @@ import UIKit class ProductAttributeViewController: UIViewController { - @IBOutlet var stackView: UIStackView! - + @IBOutlet weak var stackView: UIStackView! + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .red + stackView.backgroundColor = .white + + stackView.isHidden = true + } + + override func viewWillAppear(_ animated: Bool) { + + } + + func configureSubviews() { + stackView.isHidden = false + for subView in stackView.arrangedSubviews { + subView.backgroundColor = .green + subView.isHidden = false + } + } } diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index 7ac26156963..e0392ed5084 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -29,7 +29,9 @@ extension ProductDetailViewController { floatingPanelController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0) ]) - productAttributeController = ProductAttributeViewController.loadFromStoryboard() as ProductAttributeViewController + let storyboard = UIStoryboard(name: "ProductAttributeViewController", bundle: nil) + // swiftlint:disable:next force_cast + productAttributeController = (storyboard.instantiateViewController(withIdentifier: "ProductAttributeViewController") as! ProductAttributeViewController) floatingPanelController.set(contentViewController: productAttributeController) floatingPanelController.surfaceView.backgroundColor = .clear @@ -74,7 +76,7 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { } public var supportedPositions: Set { - return canShowDetails ? [.full, .tip] : [.tip] + return canShowDetails ? [.full, .tip] : [.half] } public func insetFor(position: FloatingPanelPosition) -> CGFloat? { @@ -88,7 +90,10 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { extension ProductDetailViewController: AttributeTableViewCellDelegate { func attributeTableViewCellTapped(_ sender: AttributeTableViewCell, _ attributeView: AttributeView) { + productAttributeController.stackView.removeAllViews() productAttributeController.stackView.addArrangedSubview(attributeView) + productAttributeController.configureSubviews() + productAttributeFloatingPanelLayout.canShowDetails = true floatingPanelController.move(to: FloatingPanelPosition.full, animated: true) } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard b/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard index 1f34dc83efa..fb53160142b 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard +++ b/Sources/Views/Products/Detail/ProductAttributes/ProductAttributeViewController.storyboard @@ -19,7 +19,7 @@ - + From d6c4ee2011d47e2e11969227b2ad49dc46095021 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Thu, 19 Nov 2020 12:32:04 -0600 Subject: [PATCH 06/20] Dynamically update insets of floatingpanel --- .../ProductAttributeViewController.swift | 50 +++++++++++++++---- ...ProductDetailViewControllerExtension.swift | 20 ++++---- .../AttributeTableViewCell.swift | 39 --------------- .../ProductAttributes/AttributeView.swift | 16 +----- 4 files changed, 51 insertions(+), 74 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index f6c36a29e68..50331f6acec 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -8,26 +8,56 @@ import UIKit class ProductAttributeViewController: UIViewController { - @IBOutlet weak var stackView: UIStackView! + var stackView = UIStackView() override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .red - stackView.backgroundColor = .white - + view.backgroundColor = .red//DEBUG stackView.isHidden = true + + view.addSubview(stackView) + // constraints + stackView.axis = NSLayoutConstraint.Axis.vertical + stackView.distribution = UIStackView.Distribution.equalSpacing + stackView.alignment = UIStackView.Alignment.center + stackView.spacing = 8.0 + + stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8.0).isActive = true + stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8.0).isActive = true + stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -8.0).isActive = true + stackView.translatesAutoresizingMaskIntoConstraints = false } override func viewWillAppear(_ animated: Bool) { - + //DEBUG } - func configureSubviews() { + func configureSubviews(with attributeView: AttributeView) { + stackView.removeAllViews() + guard let attribute = attributeView.attribute else { return } + stackView.addArrangedSubview(attributeView) + let descriptionLabel = UILabel() + descriptionLabel.numberOfLines = 0 + descriptionLabel.textAlignment = .center + descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) + + descriptionLabel.text = attribute.descriptionLong ?? attribute.descriptionShort ?? "empty description" + stackView.addArrangedSubview(descriptionLabel) stackView.isHidden = false - for subView in stackView.arrangedSubviews { - subView.backgroundColor = .green - subView.isHidden = false - } + for subView in stackView.arrangedSubviews { + subView.backgroundColor = .green//DEBUG + subView.isHidden = false + } + + } + + func totalHeight() -> CGFloat { + var total: CGFloat = 0.0 + for view in stackView.arrangedSubviews { + total += view.frame.height + } + return total } } diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index e0392ed5084..c510a56a71b 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -9,7 +9,7 @@ import FloatingPanel extension ProductDetailViewController { // MARK: - ProductAttributes FloatingPanel setup func configureFloatingPanel() { - + productAttributeController = ProductAttributeViewController() floatingPanelController = FloatingPanelController() floatingPanelController.delegate = self floatingPanelController.contentMode = .fitToBounds @@ -29,9 +29,6 @@ extension ProductDetailViewController { floatingPanelController.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0.0) ]) - let storyboard = UIStoryboard(name: "ProductAttributeViewController", bundle: nil) - // swiftlint:disable:next force_cast - productAttributeController = (storyboard.instantiateViewController(withIdentifier: "ProductAttributeViewController") as! ProductAttributeViewController) floatingPanelController.set(contentViewController: productAttributeController) floatingPanelController.surfaceView.backgroundColor = .clear @@ -56,6 +53,7 @@ extension ProductDetailViewController { extension ProductDetailViewController: FloatingPanelControllerDelegate { func floatingPanel(_ viewController: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { + productAttributeFloatingPanelLayout.contentViewOffset = productAttributeController.totalHeight() return productAttributeFloatingPanelLayout } @@ -69,20 +67,21 @@ extension ProductDetailViewController: FloatingPanelControllerDelegate { class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { - fileprivate var canShowDetails: Bool = false + var contentViewOffset: CGFloat = 0.0 public var initialPosition: FloatingPanelPosition { return .hidden } public var supportedPositions: Set { - return canShowDetails ? [.full, .tip] : [.half] + return [.full, .tip, .half] } public func insetFor(position: FloatingPanelPosition) -> CGFloat? { switch position { case .full: return 16.0 case .tip: return 112.0 + 16.0 + case .half: return 200 + 16.0 + contentViewOffset default: return nil } } @@ -90,10 +89,9 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { extension ProductDetailViewController: AttributeTableViewCellDelegate { func attributeTableViewCellTapped(_ sender: AttributeTableViewCell, _ attributeView: AttributeView) { - productAttributeController.stackView.removeAllViews() - productAttributeController.stackView.addArrangedSubview(attributeView) - productAttributeController.configureSubviews() - productAttributeFloatingPanelLayout.canShowDetails = true - floatingPanelController.move(to: FloatingPanelPosition.full, animated: true) + productAttributeController.configureSubviews(with: attributeView) + //productAttributeFloatingPanelLayout.canShowDetails = false + floatingPanelController.updateLayout() + floatingPanelController.move(to: FloatingPanelPosition.half, animated: true) } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift index 1daeeb190b3..404adbbc7ef 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeTableViewCell.swift @@ -7,7 +7,6 @@ // import UIKit -import BLTNBoard import Kingfisher import Cartography @@ -23,7 +22,6 @@ class AttributeTableViewCell: ProductDetailBaseCell { var attribute: Attribute? fileprivate var gestureRecognizer: UITapGestureRecognizer? - var bulletinManager: BLTNItemManager! override func configure(with formRow: FormRow, in viewController: FormTableViewController) { guard let attributeTableRow = formRow.value as? AttributeTableRow, @@ -76,7 +74,6 @@ class AttributeTableViewCell: ProductDetailBaseCell { } @objc func didTap(_ sender: UITapGestureRecognizer) { - print("Attribute tapped") guard let attribute = attribute else { return } @@ -87,39 +84,3 @@ class AttributeTableViewCell: ProductDetailBaseCell { delegate?.attributeTableViewCellTapped(self, attributeView) } } - -class AttributeBLTNPageItem: BLTNPageItem { - var attribute: Attribute? - var iconImageBackgroundColor: UIColor? - var attributeView: AttributeView? - - override func makeHeaderViews(with interfaceBuilder: BLTNInterfaceBuilder) -> [UIView]? { - let containerA = UIView() - containerA.backgroundColor = iconImageBackgroundColor - - if let attributeV = attributeView { - containerA.addSubview(attributeV) - - constrain(attributeV, containerA) { (attributeV, containerA) in - containerA.width == attributeV.width - containerA.height == attributeV.height - attributeV.edges == containerA.edges - - } - - var views: [UIView] = [containerA] - - if let attribute = attribute { - let descriptionLabel = UILabel() - descriptionLabel.numberOfLines = 0 - descriptionLabel.textAlignment = .center - descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) - - descriptionLabel.text = attribute.descriptionLong ?? attribute.descriptionShort ?? "empty description" - views.append(descriptionLabel) - } - return views - } - return nil - } -} diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 0f2ee89239d..8ddfdb0180d 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -7,7 +7,6 @@ // import UIKit -import BLTNBoard import Kingfisher import Cartography @@ -36,8 +35,8 @@ import Cartography } func setIconImageView(imageURL: String?) { - guard let icon = imageURL, - let url = URL(string: icon) + guard let icon = imageURL, let attribute = attribute, + let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png")//DEBUG should be "icon" else { iconImageView.isHidden = false return @@ -46,17 +45,6 @@ import Cartography iconImageView.kf.setImage(with: url) iconImageView.isHidden = false } - - var bulletinManager: BLTNItemManager! - - deinit { - if bulletinManager != nil { - if bulletinManager.isShowingBulletin { - bulletinManager.dismissBulletin(animated: true) - } - bulletinManager = nil - } - } } protocol formatAttributedString { From 9c52e8843a5bf447668a646e036f304a17b8cfb7 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sat, 21 Nov 2020 23:28:10 -0600 Subject: [PATCH 07/20] Dynamically update FloatingPanel view insets --- .../ProductAttributeViewController.swift | 27 ++++++++++++++----- ...ProductDetailViewControllerExtension.swift | 13 ++++----- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index 50331f6acec..4d05ec0d86e 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -6,9 +6,13 @@ // import UIKit +import FloatingPanel class ProductAttributeViewController: UIViewController { var stackView = UIStackView() + var attributeView: AttributeView? + var descriptionLabel: UILabel! + var totalStackViewHeight: CGFloat = 0.0 override func viewDidLoad() { super.viewDidLoad() @@ -30,15 +34,20 @@ class ProductAttributeViewController: UIViewController { stackView.translatesAutoresizingMaskIntoConstraints = false } - override func viewWillAppear(_ animated: Bool) { - //DEBUG + override func viewDidLayoutSubviews() { + let newHeight = getTotalHeight() + if newHeight != totalStackViewHeight, let floatingPanelVC = parent as? FloatingPanelController { + totalStackViewHeight = newHeight + floatingPanelVC.updateLayout() + } } - func configureSubviews(with attributeView: AttributeView) { - stackView.removeAllViews() - guard let attribute = attributeView.attribute else { return } + func configureSubviews() { + resetView() + guard let attributeView = attributeView, + let attribute = attributeView.attribute else { return } stackView.addArrangedSubview(attributeView) - let descriptionLabel = UILabel() + descriptionLabel = UILabel() descriptionLabel.numberOfLines = 0 descriptionLabel.textAlignment = .center descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) @@ -53,7 +62,11 @@ class ProductAttributeViewController: UIViewController { } - func totalHeight() -> CGFloat { + func resetView() { + stackView.removeAllViews() + } + + private func getTotalHeight() -> CGFloat { var total: CGFloat = 0.0 for view in stackView.arrangedSubviews { total += view.frame.height diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index c510a56a71b..cf1c3d60f98 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -46,6 +46,8 @@ extension ProductDetailViewController { // MARK: - Gesture recognizers @objc func hideSummaryView(_ sender: UISwipeGestureRecognizer) { + productAttributeController.attributeView = nil + productAttributeController.resetView() floatingPanelController.move(to: FloatingPanelPosition.hidden, animated: true) } @@ -53,7 +55,7 @@ extension ProductDetailViewController { extension ProductDetailViewController: FloatingPanelControllerDelegate { func floatingPanel(_ viewController: FloatingPanelController, layoutFor newCollection: UITraitCollection) -> FloatingPanelLayout? { - productAttributeFloatingPanelLayout.contentViewOffset = productAttributeController.totalHeight() + productAttributeFloatingPanelLayout.contentViewOffset = productAttributeController.totalStackViewHeight return productAttributeFloatingPanelLayout } @@ -74,14 +76,14 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { } public var supportedPositions: Set { - return [.full, .tip, .half] + return [.half] } public func insetFor(position: FloatingPanelPosition) -> CGFloat? { switch position { case .full: return 16.0 case .tip: return 112.0 + 16.0 - case .half: return 200 + 16.0 + contentViewOffset + case .half: return contentViewOffset + 24.0 default: return nil } } @@ -89,9 +91,8 @@ class ProductAttributeFloatingPanelLayout: FloatingPanelLayout { extension ProductDetailViewController: AttributeTableViewCellDelegate { func attributeTableViewCellTapped(_ sender: AttributeTableViewCell, _ attributeView: AttributeView) { - productAttributeController.configureSubviews(with: attributeView) - //productAttributeFloatingPanelLayout.canShowDetails = false - floatingPanelController.updateLayout() + productAttributeController.attributeView = attributeView + productAttributeController.configureSubviews() floatingPanelController.move(to: FloatingPanelPosition.half, animated: true) } } From ef3dbd1c6d6628633b4654b8ce2eaf6507bd8ef8 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sun, 22 Nov 2020 18:01:07 -0600 Subject: [PATCH 08/20] Dismiss FloatingPanel from SummaryFormTableVC --- .../ProductAttributes/ProductAttributeViewController.swift | 1 + .../ProductDetailViewControllerExtension.swift | 4 ++++ .../Products/Detail/ProductDetailViewController.swift | 2 +- .../Detail/Summary/SummaryFormTableViewController.swift | 7 +++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index 4d05ec0d86e..02d616303b5 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -35,6 +35,7 @@ class ProductAttributeViewController: UIViewController { } override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() let newHeight = getTotalHeight() if newHeight != totalStackViewHeight, let floatingPanelVC = parent as? FloatingPanelController { totalStackViewHeight = newHeight diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index cf1c3d60f98..8cf948d585f 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -46,6 +46,10 @@ extension ProductDetailViewController { // MARK: - Gesture recognizers @objc func hideSummaryView(_ sender: UISwipeGestureRecognizer) { + hideFloatingPanel() + } + + func hideFloatingPanel() { productAttributeController.attributeView = nil productAttributeController.resetView() floatingPanelController.move(to: FloatingPanelPosition.hidden, animated: true) diff --git a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift index f37b6056b4a..51489352aad 100644 --- a/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductDetailViewController.swift @@ -49,7 +49,7 @@ class ProductDetailViewController: ButtonBarPagerTabStripViewController, DataMan } } - // set up floatingPanel + // set up floatingPanel for ProductAttributes UI configureFloatingPanel() setUserAgent() diff --git a/Sources/ViewControllers/Products/Detail/Summary/SummaryFormTableViewController.swift b/Sources/ViewControllers/Products/Detail/Summary/SummaryFormTableViewController.swift index 330cba982d6..3ddfc6e6bc8 100644 --- a/Sources/ViewControllers/Products/Detail/Summary/SummaryFormTableViewController.swift +++ b/Sources/ViewControllers/Products/Detail/Summary/SummaryFormTableViewController.swift @@ -22,6 +22,13 @@ class SummaryFormTableViewController: FormTableViewController { super.init(coder: aDecoder) } + override func viewWillDisappear(_ animated: Bool) { + //dismiss floating panel + if let productDetailVC = delegate as? ProductDetailViewController { + productDetailVC.hideFloatingPanel() + } + } + override func getCell(for formRow: FormRow) -> UITableViewCell { if formRow.cellType is SummaryHeaderCell.Type, let product = formRow.value as? Product { let cell = tableView.dequeueReusableCell(withIdentifier: formRow.cellType.identifier) as! HostedViewCell // swiftlint:disable:this force_cast From ca7a67303b5292ff4632ac1a4637c20d2f3c952f Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Wed, 25 Nov 2020 00:36:11 -0600 Subject: [PATCH 09/20] NOT READY FOR PROD. Wrap text around the image Add the frame of the image to the exclusion path of the text view. Create func getIconAspectConstraint() to get the dynamic aspect ratio of the image in code so we can dynamically adjust the imageView height and width. Place image related code in completion handle of KingFisher setImage(with: resource) to ensure the adjustments are attempted after the image is retrieved. Resolves: #707 --- .../ProductAttributeViewController.swift | 4 +- .../ProductAttributes/AttributeView.swift | 42 ++++++++++++++++--- .../ProductAttributes/AttributeView.xib | 39 ++++++++--------- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index 02d616303b5..461c47fe805 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -17,7 +17,7 @@ class ProductAttributeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .red//DEBUG + view.backgroundColor = .red// FIXME: coloring for UI DEBUG stackView.isHidden = true view.addSubview(stackView) @@ -57,7 +57,7 @@ class ProductAttributeViewController: UIViewController { stackView.addArrangedSubview(descriptionLabel) stackView.isHidden = false for subView in stackView.arrangedSubviews { - subView.backgroundColor = .green//DEBUG + subView.backgroundColor = .green// FIXME: coloring for UI DEBUG subView.isHidden = false } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 8ddfdb0180d..439cc8463d7 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -20,11 +20,14 @@ import Cartography func configure(_ attribute: Attribute) { self.layer.cornerRadius = 5 self.attribute = attribute + setIconImageView(imageURL: attribute.iconUrl) if let label = attribute.name, let description = attribute.descriptionShort ?? attribute.title { - let text = AttributedStringFormatter.formatAttributedText(label: label, description: description) + let text = AttributedStringFormatter.formatAttributedText(label: label, description1: description) descriptionShort.attributedText = text } + + } static func loadFromNib() -> AttributeView { @@ -36,28 +39,57 @@ import Cartography func setIconImageView(imageURL: String?) { guard let icon = imageURL, let attribute = attribute, - let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png")//DEBUG should be "icon" + let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png") + // FIXME: DEBUG STAND IN VALUES, should be "icon" from attribute else { iconImageView.isHidden = false return } iconImageView.kf.indicatorType = .activity - iconImageView.kf.setImage(with: url) + iconImageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil) { [weak self] _ in + if let aspectRatioConstraint = self?.getIconAspectConstraint() { + self?.addConstraint(aspectRatioConstraint) + } + + // wrap text around the image + self?.layoutIfNeeded() + let exclusionPathFrame = self?.convert((self?.iconImageView.frame)!, to: self?.descriptionShort) + let iconImagePath = UIBezierPath(rect: exclusionPathFrame!) + self?.descriptionShort.textContainer.exclusionPaths.append(iconImagePath) + } iconImageView.isHidden = false } + + func getIconAspectConstraint() -> NSLayoutConstraint? { + guard let imageView = iconImageView, let image = imageView.image else { return nil } + let height = image.size.width + let width = image.size.height + var scale: CGFloat = 1.0 + if height > 100 || width > 100 { + scale = 0.25 + } + var aspectRatio = width / height + aspectRatio *= scale + + let aspectConstraint = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: aspectRatio, constant: 0) + + return aspectConstraint + } } protocol formatAttributedString { static var boldWordsPattern: String {get} - static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? + static func formatAttributedText(label: String, description1: String) -> NSMutableAttributedString? static func makeWordsBold(for originalText: NSAttributedString) -> NSAttributedString } class AttributedStringFormatter: formatAttributedString { static var boldWordsPattern: String { return "(_\\w+_)" } - static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? { + static func formatAttributedText(label: String, description1: String) -> NSMutableAttributedString? { + // TODO: change description1 parameter back to description and remove below temp var 'description' + var description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body).pointSize) var bold: [NSAttributedString.Key: Any] = [:] if #available(iOS 13.0, *) { diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index 69ee1556323..e0a2f16a40e 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -13,37 +13,32 @@ - - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - - + + - + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + - + - - + + + - + + - - - - + + + From bd472c69d37150e517c7eea04d8e0e183b818f95 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Wed, 25 Nov 2020 15:56:06 -0600 Subject: [PATCH 10/20] Text wraps image but only 1st time --- .../ProductAttributes/AttributeView.swift | 19 +++++++------------ .../ProductAttributes/AttributeView.xib | 11 +++++------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 439cc8463d7..911a8c67f1c 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -26,8 +26,6 @@ import Cartography let text = AttributedStringFormatter.formatAttributedText(label: label, description1: description) descriptionShort.attributedText = text } - - } static func loadFromNib() -> AttributeView { @@ -40,22 +38,23 @@ import Cartography func setIconImageView(imageURL: String?) { guard let icon = imageURL, let attribute = attribute, let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png") - // FIXME: DEBUG STAND IN VALUES, should be "icon" from attribute + // FIXME: DEBUG STAND IN VALUES, should be "icon" from attribute's imageURL else { iconImageView.isHidden = false return } iconImageView.kf.indicatorType = .activity iconImageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil) { [weak self] _ in - if let aspectRatioConstraint = self?.getIconAspectConstraint() { - self?.addConstraint(aspectRatioConstraint) - } +// if let aspectRatioConstraint = self?.getIconAspectConstraint() { +// self?.addConstraint(aspectRatioConstraint) +// } // wrap text around the image self?.layoutIfNeeded() let exclusionPathFrame = self?.convert((self?.iconImageView.frame)!, to: self?.descriptionShort) let iconImagePath = UIBezierPath(rect: exclusionPathFrame!) self?.descriptionShort.textContainer.exclusionPaths.append(iconImagePath) + self?.layoutSubviews() } iconImageView.isHidden = false } @@ -64,12 +63,8 @@ import Cartography guard let imageView = iconImageView, let image = imageView.image else { return nil } let height = image.size.width let width = image.size.height - var scale: CGFloat = 1.0 - if height > 100 || width > 100 { - scale = 0.25 - } - var aspectRatio = width / height - aspectRatio *= scale + + let aspectRatio = width / height let aspectConstraint = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: aspectRatio, constant: 0) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index e0a2f16a40e..c14ce645ebd 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -14,7 +14,7 @@ - + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. @@ -23,19 +23,18 @@ - + - - - - + + + From 8a5ad31760679a3e1bdbd533c1c92a84b22b3ddf Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Mon, 3 May 2021 23:05:57 -0500 Subject: [PATCH 11/20] Anchor icon in AttributeView to top corner so text wraps --- .../ProductAttributes/AttributeView.swift | 25 +++---------------- .../ProductAttributes/AttributeView.xib | 20 +++++++-------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 911a8c67f1c..ca38312b22b 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -36,19 +36,14 @@ import Cartography } func setIconImageView(imageURL: String?) { - guard let icon = imageURL, let attribute = attribute, - let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png") - // FIXME: DEBUG STAND IN VALUES, should be "icon" from attribute's imageURL - else { + guard let iconURL = imageURL, let attribute = attribute else { iconImageView.isHidden = false return } - iconImageView.kf.indicatorType = .activity - iconImageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil) { [weak self] _ in -// if let aspectRatioConstraint = self?.getIconAspectConstraint() { -// self?.addConstraint(aspectRatioConstraint) -// } + let url = URL(string: "https://static.openfoodfacts.org/images/icons/\(attribute.id!.contains("organic") ? "vegan-status-unknown" : "nutrient-level-salt-medium").png") + // FIXME: DEBUG STAND IN VALUES, should be "icon" from attribute's imageURL + iconImageView.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil) { [weak self] _ in // wrap text around the image self?.layoutIfNeeded() let exclusionPathFrame = self?.convert((self?.iconImageView.frame)!, to: self?.descriptionShort) @@ -58,18 +53,6 @@ import Cartography } iconImageView.isHidden = false } - - func getIconAspectConstraint() -> NSLayoutConstraint? { - guard let imageView = iconImageView, let image = imageView.image else { return nil } - let height = image.size.width - let width = image.size.height - - let aspectRatio = width / height - - let aspectConstraint = NSLayoutConstraint(item: imageView, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: aspectRatio, constant: 0) - - return aspectConstraint - } } protocol formatAttributedString { diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index c14ce645ebd..3dd6479ae94 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -1,5 +1,5 @@ - + @@ -10,11 +10,11 @@ - + - + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. @@ -23,21 +23,21 @@ - + - - + + - + - - + + - + From 28b335e0586342399df53c11259f33120e9c37c2 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Tue, 4 May 2021 23:04:34 -0500 Subject: [PATCH 12/20] Switch ImageView to UIWebView, add code to scale SVGs, regex search in SVG's HTML to get dimensions --- Sources/Extensions/String.swift | 15 +++ Sources/Models/ErrorCodes.swift | 1 + .../ProductAttributes/AttributeView.swift | 98 ++++++++++++++++--- .../ProductAttributes/AttributeView.xib | 20 ++-- Tests/Extensions/StringTests.swift | 5 + 5 files changed, 114 insertions(+), 25 deletions(-) diff --git a/Sources/Extensions/String.swift b/Sources/Extensions/String.swift index 92335652ccf..fbef17528b1 100644 --- a/Sources/Extensions/String.swift +++ b/Sources/Extensions/String.swift @@ -39,6 +39,21 @@ extension String { return decoded ?? self } + func searchBetweenRegexes(from endOfRegexA: String, to endOfRegexB: String) throws -> String? { + guard endOfRegexB.contains(endOfRegexA) else { + throw NSError(domain: "RegexB must contain RegexA to find the difference between them", code: Errors.codes.regexSearchStringError.rawValue) + } + + let startIndex = self.range(of:endOfRegexA, options: .regularExpression)?.upperBound + let endIndex = self.range(of:endOfRegexB, options: .regularExpression)?.upperBound + if let start = startIndex, let end = endIndex { + let result = self[start.. String? { + return webView.stringByEvaluatingJavaScript(from: "document.documentElement.outerHTML") + } + + public func getSVGdimensions(from html: String?) -> CGSize? { + var contentSize: CGSize? + let searchString = """ + + """ + let regexpA = "width=\"" + let regexpB = "width=\"\\d*" + let regexpC = "height=\"" + let regexpD = "height=\"\\d*" + do { + var width: CGFloat = 0 + var height: CGFloat = 0 + if let widthString = try searchString.searchBetweenRegexes(from: regexpA, to: regexpB) { + if let widthFloat = Float(widthString) { + width = CGFloat(widthFloat) + } + } + + if let heightString = try searchString.searchBetweenRegexes(from: regexpC, to: regexpD) { + if let heightFloat = Float(heightString) { + height = CGFloat(heightFloat) + } + } + + if width > 0 && height > 0 { + contentSize = CGSize(width: width, height: height) + return contentSize + } + } catch { + print(error.localizedDescription) + return nil } - iconImageView.isHidden = false + + return nil + } +} + +extension AttributeView: UIWebViewDelegate { + func webViewDidFinishLoad(_ webView: UIWebView) { + wrapText(around: webView) + scaleWebViewForSVGcontent(webView) } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index 3dd6479ae94..ab21f2743fc 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -22,20 +22,22 @@ - - + + + - - + + - + + - + + - @@ -43,9 +45,9 @@ - + - + diff --git a/Tests/Extensions/StringTests.swift b/Tests/Extensions/StringTests.swift index e32369d88cd..4a3c02bc699 100644 --- a/Tests/Extensions/StringTests.swift +++ b/Tests/Extensions/StringTests.swift @@ -23,4 +23,9 @@ class StringTests: XCTestCase { func testIsNumberReturnsFalseWhenStringIsNotNumber() { XCTAssertFalse("a".isNumber()) } + + func testAttributeGetWidthHeightFromSVG_HTML() { + let attribute = AttributeView(frame: CGRect()) + XCTAssertNotNil(attribute.getSVGdimensions(from: "Test")) + } } From 1a89da1f98d7193978e6f68fa592278217d49526 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Thu, 6 May 2021 18:25:13 -0500 Subject: [PATCH 13/20] Fix SVG icon not centered --- .../ProductAttributes/AttributeView.swift | 31 ++++++++++--------- Tests/Extensions/StringTests.swift | 7 ++++- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index d52d74067a7..89d212e47bf 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -53,24 +53,27 @@ import Cartography } private func scaleWebViewForSVGcontent(_ webView: UIWebView) { - let contentSize = webView.scrollView.contentSize - let webViewSize = webView.frame.size - print(webViewSize)//debug - let scaleFactor = webViewSize.width / contentSize.width - - webView.scrollView.minimumZoomScale = scaleFactor - webView.scrollView.maximumZoomScale = scaleFactor - webView.scrollView.zoomScale = scaleFactor - - webView.scalesPageToFit = false + if let contentSize = getSVGdimensions(from: getHTMLfrom(webView: webView)) { + let webViewSize = webView.frame.size + print("webviewsize \(webViewSize)")//DEBUG + print("webview bounds \(webView.bounds.size)")//DEBUG + webView.scrollView.contentOffset = CGPoint(x: 0.0, y: 0.0) + let scaleFactor = webViewSize.width / contentSize.width + + webView.scrollView.minimumZoomScale = scaleFactor + webView.scrollView.maximumZoomScale = scaleFactor + webView.scrollView.zoomScale = scaleFactor + + webView.scalesPageToFit = false + } } private func wrapText(around webview: UIWebView) { - layoutIfNeeded() +// layoutIfNeeded() let exclusionPathFrame = convert(webview.frame, to: descriptionShort) let iconImagePath = UIBezierPath(rect: exclusionPathFrame) descriptionShort.textContainer.exclusionPaths.append(iconImagePath) - layoutSubviews() +// layoutSubviews() } private func getHTMLfrom(webView: UIWebView) -> String? { @@ -79,9 +82,7 @@ import Cartography public func getSVGdimensions(from html: String?) -> CGSize? { var contentSize: CGSize? - let searchString = """ - - """ + guard let searchString = html else { return nil } let regexpA = "width=\"" let regexpB = "width=\"\\d*" let regexpC = "height=\"" diff --git a/Tests/Extensions/StringTests.swift b/Tests/Extensions/StringTests.swift index 4a3c02bc699..f2f5386cf69 100644 --- a/Tests/Extensions/StringTests.swift +++ b/Tests/Extensions/StringTests.swift @@ -26,6 +26,11 @@ class StringTests: XCTestCase { func testAttributeGetWidthHeightFromSVG_HTML() { let attribute = AttributeView(frame: CGRect()) - XCTAssertNotNil(attribute.getSVGdimensions(from: "Test")) + let searchString = """ + + """ + let contentCGSize = attribute.getSVGdimensions(from: searchString) + XCTAssertNotNil(contentCGSize) + XCTAssert(contentCGSize?.width == 24 && contentCGSize?.height == 24) } } From 04b4ca10c11d2748e76260ca5dc676656deba525 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Fri, 7 May 2021 18:00:19 -0500 Subject: [PATCH 14/20] Add activity spinner to show loading Attr. Icon --- .../ProductAttributeViewController.swift | 5 +--- .../ProductAttributes/AttributeView.swift | 25 ++++++++----------- .../ProductAttributes/AttributeView.xib | 11 ++++++-- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index 461c47fe805..25d27f76537 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -17,7 +17,6 @@ class ProductAttributeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .red// FIXME: coloring for UI DEBUG stackView.isHidden = true view.addSubview(stackView) @@ -50,14 +49,12 @@ class ProductAttributeViewController: UIViewController { stackView.addArrangedSubview(attributeView) descriptionLabel = UILabel() descriptionLabel.numberOfLines = 0 - descriptionLabel.textAlignment = .center + descriptionLabel.textAlignment = .left descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) - descriptionLabel.text = attribute.descriptionLong ?? attribute.descriptionShort ?? "empty description" stackView.addArrangedSubview(descriptionLabel) stackView.isHidden = false for subView in stackView.arrangedSubviews { - subView.backgroundColor = .green// FIXME: coloring for UI DEBUG subView.isHidden = false } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index 89d212e47bf..df000cf306e 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -14,6 +14,7 @@ import Cartography @IBOutlet weak var iconWebView: UIWebView! @IBOutlet weak var descriptionShort: UITextView! + @IBOutlet weak var activitySpinnerForIcon: UIActivityIndicatorView! var attribute: Attribute? @@ -45,19 +46,18 @@ import Cartography } if let url = URL(string: iconURL) { + iconWebView.isHidden = false + wrapText(around: iconWebView) + activitySpinnerForIcon.startAnimating() + activitySpinnerForIcon.isHidden = false let imageRequest = URLRequest(url: url) iconWebView.loadRequest(imageRequest) } - - iconWebView.isHidden = false } private func scaleWebViewForSVGcontent(_ webView: UIWebView) { if let contentSize = getSVGdimensions(from: getHTMLfrom(webView: webView)) { let webViewSize = webView.frame.size - print("webviewsize \(webViewSize)")//DEBUG - print("webview bounds \(webView.bounds.size)")//DEBUG - webView.scrollView.contentOffset = CGPoint(x: 0.0, y: 0.0) let scaleFactor = webViewSize.width / contentSize.width webView.scrollView.minimumZoomScale = scaleFactor @@ -69,11 +69,9 @@ import Cartography } private func wrapText(around webview: UIWebView) { -// layoutIfNeeded() let exclusionPathFrame = convert(webview.frame, to: descriptionShort) let iconImagePath = UIBezierPath(rect: exclusionPathFrame) descriptionShort.textContainer.exclusionPaths.append(iconImagePath) -// layoutSubviews() } private func getHTMLfrom(webView: UIWebView) -> String? { @@ -117,24 +115,23 @@ import Cartography extension AttributeView: UIWebViewDelegate { func webViewDidFinishLoad(_ webView: UIWebView) { - wrapText(around: webView) + activitySpinnerForIcon.stopAnimating() scaleWebViewForSVGcontent(webView) } } -protocol formatAttributedString { +protocol FormatAttributedString { static var boldWordsPattern: String {get} - static func formatAttributedText(label: String, description1: String) -> NSMutableAttributedString? + static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? static func makeWordsBold(for originalText: NSAttributedString) -> NSAttributedString } -class AttributedStringFormatter: formatAttributedString { +class AttributedStringFormatter: FormatAttributedString { static var boldWordsPattern: String { return "(_\\w+_)" } - static func formatAttributedText(label: String, description1: String) -> NSMutableAttributedString? { - // TODO: change description1 parameter back to description and remove below temp var 'description' - var description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." + static func formatAttributedText(label: String, description: String) -> NSMutableAttributedString? { + let headline = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.headline), size: UIFontDescriptor.preferredFontDescriptor(withTextStyle: UIFont.TextStyle.body).pointSize) var bold: [NSAttributedString.Key: Any] = [:] if #available(iOS 13.0, *) { diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index ab21f2743fc..5a7c7ff817b 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -23,7 +23,7 @@ - + @@ -31,19 +31,26 @@ + - + + + + From 5a3bcba91fe0b5b7a9de14143a1bc0b6ceb2ff11 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Fri, 14 May 2021 18:59:21 -0500 Subject: [PATCH 15/20] Fix AttributeView heighth on FloatingPanelVC --- .../ProductAttributeViewController.swift | 7 +++++-- ...ProductDetailViewControllerExtension.swift | 2 +- .../ProductAttributes/AttributeView.swift | 2 +- .../ProductAttributes/AttributeView.xib | 21 +++++++++++-------- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift index 25d27f76537..2f8cceb66a4 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductAttributeViewController.swift @@ -22,8 +22,8 @@ class ProductAttributeViewController: UIViewController { view.addSubview(stackView) // constraints stackView.axis = NSLayoutConstraint.Axis.vertical - stackView.distribution = UIStackView.Distribution.equalSpacing - stackView.alignment = UIStackView.Alignment.center + stackView.distribution = UIStackView.Distribution.fill + stackView.alignment = UIStackView.Alignment.fill stackView.spacing = 8.0 stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true @@ -46,8 +46,11 @@ class ProductAttributeViewController: UIViewController { resetView() guard let attributeView = attributeView, let attribute = attributeView.attribute else { return } + attributeView.setContentHuggingPriority(.init(rawValue: 250), for: .vertical) stackView.addArrangedSubview(attributeView) + descriptionLabel = UILabel() + descriptionLabel.setContentHuggingPriority(.init(rawValue: 251), for: .vertical) descriptionLabel.numberOfLines = 0 descriptionLabel.textAlignment = .left descriptionLabel.font = UIFont.boldSystemFont(ofSize: 17) diff --git a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift index 8cf948d585f..026fc1c3bba 100644 --- a/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift +++ b/Sources/ViewControllers/Products/Detail/ProductAttributes/ProductDetailViewControllerExtension.swift @@ -31,7 +31,7 @@ extension ProductDetailViewController { floatingPanelController.set(contentViewController: productAttributeController) - floatingPanelController.surfaceView.backgroundColor = .clear + floatingPanelController.surfaceView.backgroundColor = .white floatingPanelController.surfaceView.cornerRadius = 9.0 floatingPanelController.surfaceView.shadowHidden = false // Add a gesture to hide the floating panel diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift index df000cf306e..b4dbe03a26c 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.swift @@ -26,7 +26,7 @@ import Cartography setIconWebView(imageURL: attribute.iconUrl) if let label = attribute.name, let description = attribute.descriptionShort ?? attribute.title { - let text = AttributedStringFormatter.formatAttributedText(label: label, description1: description) + let text = AttributedStringFormatter.formatAttributedText(label: label, description: description) descriptionShort.attributedText = text } } diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index 5a7c7ff817b..803fd08c018 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -10,31 +10,34 @@ - + - + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + - + - - + + - From 4c9a315e0f9e8948e0305de5950a3b650144655e Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Wed, 26 May 2021 14:57:48 -0500 Subject: [PATCH 16/20] Cleanup minor SwiftLint warnings --- Sources/Extensions/String.swift | 4 ++-- .../Products/Detail/ProductDetailViewController.swift | 2 +- .../Detail/Summary/SummaryFormTableViewController.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Extensions/String.swift b/Sources/Extensions/String.swift index fbef17528b1..0577a6e4b1a 100644 --- a/Sources/Extensions/String.swift +++ b/Sources/Extensions/String.swift @@ -44,8 +44,8 @@ extension String { throw NSError(domain: "RegexB must contain RegexA to find the difference between them", code: Errors.codes.regexSearchStringError.rawValue) } - let startIndex = self.range(of:endOfRegexA, options: .regularExpression)?.upperBound - let endIndex = self.range(of:endOfRegexB, options: .regularExpression)?.upperBound + let startIndex = self.range(of: endOfRegexA, options: .regularExpression)?.upperBound + let endIndex = self.range(of: endOfRegexB, options: .regularExpression)?.upperBound if let start = startIndex, let end = endIndex { let result = self[start.. Date: Wed, 26 May 2021 17:12:17 -0500 Subject: [PATCH 17/20] Move activitySpinner back to top for visibility --- .../Products/Detail/ProductAttributes/AttributeView.xib | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib index 803fd08c018..87fbcc5c77d 100644 --- a/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib +++ b/Sources/Views/Products/Detail/ProductAttributes/AttributeView.xib @@ -25,10 +25,6 @@ - @@ -38,6 +34,10 @@ + From 6ea41110184a264209f3bf9e9d3bb2127c5ebbd3 Mon Sep 17 00:00:00 2001 From: Alex Beaty Date: Sat, 29 May 2021 18:46:11 -0500 Subject: [PATCH 18/20] Fix mem leak: IngrdAnlysCell retained SummaryForm --- .../Detail/Summary/IngredientsAnalysisTableViewCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Views/Products/Detail/Summary/IngredientsAnalysisTableViewCell.swift b/Sources/Views/Products/Detail/Summary/IngredientsAnalysisTableViewCell.swift index e2a7df8a72c..77412a202ff 100644 --- a/Sources/Views/Products/Detail/Summary/IngredientsAnalysisTableViewCell.swift +++ b/Sources/Views/Products/Detail/Summary/IngredientsAnalysisTableViewCell.swift @@ -12,7 +12,7 @@ class IngredientsAnalysisTableViewCell: ProductDetailBaseCell { @IBOutlet weak var stackView: UIStackView! - var viewController: FormTableViewController? + weak var viewController: FormTableViewController? override func configure(with formRow: FormRow, in viewController: FormTableViewController) { guard let product = formRow.value as? Product else { return } From 42ae84ad7f7c1c6d549123cba3de6cc2f7d1e772 Mon Sep 17 00:00:00 2001 From: Pierre Slamich Date: Sat, 12 Jun 2021 17:49:31 +0200 Subject: [PATCH 19/20] Update ios-release.yml --- .github/workflows/ios-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios-release.yml b/.github/workflows/ios-release.yml index cd0321ef629..3ca19e9868e 100644 --- a/.github/workflows/ios-release.yml +++ b/.github/workflows/ios-release.yml @@ -3,7 +3,7 @@ name: TestFlight release on: push: branches: - - 'release/*' + - '*' jobs: testflight-release: From 246deecca17927e3af5d14f630dffa08b0019455 Mon Sep 17 00:00:00 2001 From: Pierre Slamich Date: Sat, 12 Jun 2021 17:51:20 +0200 Subject: [PATCH 20/20] Update ios-release.yml --- .github/workflows/ios-release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ios-release.yml b/.github/workflows/ios-release.yml index 3ca19e9868e..54ab75bb278 100644 --- a/.github/workflows/ios-release.yml +++ b/.github/workflows/ios-release.yml @@ -1,9 +1,6 @@ name: TestFlight release -on: - push: - branches: - - '*' +on: [push, pull_request] jobs: testflight-release: