// // AppContentViewController.swift // AltStore // // Created by Riley Testut on 7/22/19. // Copyright © 2019 Riley Testut. All rights reserved. // import UIKit import AltStoreCore import Roxas import Nuke extension AppContentViewController { private enum Row: Int, CaseIterable { case subtitle case screenshots case description case versionDescription case permissions } } final class AppContentViewController: UITableViewController { var app: StoreApp! // private lazy var screenshotsDataSource = self.makeScreenshotsDataSource() private lazy var dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeStyle = .none return dateFormatter }() private lazy var byteCountFormatter: ByteCountFormatter = { let formatter = ByteCountFormatter() return formatter }() @IBOutlet private var subtitleLabel: UILabel! @IBOutlet private var descriptionTextView: CollapsingTextView! // @IBOutlet private var versionDescriptionTextView: CollapsingTextView! @IBOutlet private var versionDescriptionTextView: CollapsingMarkdownView! @IBOutlet private var versionLabel: UILabel! @IBOutlet private var versionDateLabel: UILabel! @IBOutlet private var sizeLabel: UILabel! @IBOutlet private(set) var appScreenshotsViewController: AppScreenshotsViewController! @IBOutlet private var appScreenshotsHeightConstraint: NSLayoutConstraint! @IBOutlet private(set) var appDetailCollectionViewController: AppDetailCollectionViewController! @IBOutlet private var appDetailCollectionViewHeightConstraint: NSLayoutConstraint! // // override func viewDidLoad() // { // super.viewDidLoad() // // self.tableView.contentInset.bottom = 20 // // self.subtitleLabel.text = self.app.subtitle // self.descriptionTextView.text = self.app.localizedDescription // // if let version = self.app.latestAvailableVersion // { // self.versionDescriptionTextView.text = version.localizedDescription ?? "nil" // self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion) // self.versionDateLabel.text = Date().relativeDateString(since: version.date) // self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: version.size) // } // else // { // self.versionDescriptionTextView.text = "nil" // self.versionLabel.text = nil // self.versionDateLabel.text = nil // self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: 0) // } // // self.descriptionTextView.maximumNumberOfLines = 5 // self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered) // // self.versionDescriptionTextView.maximumNumberOfLines = 3 // self.versionDescriptionTextView.toggleButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered) // } override func viewDidLoad() { super.viewDidLoad() self.tableView.contentInset.bottom = 20 self.subtitleLabel.text = self.app.subtitle self.descriptionTextView.text = self.app.localizedDescription if let version = self.app.latestAvailableVersion { self.versionDescriptionTextView.text = version.localizedDescription ?? "nil" self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion) self.versionDateLabel.text = Date().relativeDateString(since: version.date) self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: version.size, countStyle: .file) } else { self.versionDescriptionTextView.text = "nil" self.versionLabel.text = nil self.versionDateLabel.text = nil self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: 0, countStyle: .file) } // Set maximum number of lines (no extra target-action needed) self.descriptionTextView.maximumNumberOfLines = 5 self.versionDescriptionTextView.maximumNumberOfLines = 3 // Instead of adding another target for toggle, set the didToggleCollapse callback: self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered) self.versionDescriptionTextView.didToggleCollapse = { [weak self] in guard let self = self else { return } UIView.animate(withDuration: 0.25) { self.tableView.beginUpdates() self.tableView.endUpdates() } } } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() var needsTableViewUpdate = false let screenshotsHeight = self.appScreenshotsViewController.collectionView.contentSize.height if self.appScreenshotsHeightConstraint.constant != screenshotsHeight && screenshotsHeight > 0 { self.appScreenshotsHeightConstraint.constant = screenshotsHeight needsTableViewUpdate = true } let permissionsHeight = self.appDetailCollectionViewController.collectionView.contentSize.height if self.appDetailCollectionViewHeightConstraint.constant != permissionsHeight && permissionsHeight > 0 { self.appDetailCollectionViewHeightConstraint.constant = permissionsHeight needsTableViewUpdate = true } if needsTableViewUpdate { UIView.performWithoutAnimation { // Update row height without animation. self.tableView.beginUpdates() self.tableView.endUpdates() } } } } private extension AppContentViewController { @IBSegueAction func makeAppScreenshotsViewController(_ coder: NSCoder, sender: Any?) -> UIViewController? { let appScreenshotsViewController = AppScreenshotsViewController(app: self.app, coder: coder) self.appScreenshotsViewController = appScreenshotsViewController return appScreenshotsViewController } func makePermissionsDataSource() -> RSTArrayCollectionViewDataSource { let dataSource = RSTArrayCollectionViewDataSource(items: Array(self.app.permissions)) dataSource.cellConfigurationHandler = { (cell, permission, indexPath) in let cell = cell as! PermissionCollectionViewCell // cell.button.setImage(permission.type.icon, for: .normal) // cell.button.tintColor = .label // cell.textLabel.text = permission.type.localizedShortName ?? permission.type.localizedName let icon = UIImage(systemName: permission.symbolName ?? "lock") cell.button.setImage(icon, for: .normal) cell.textLabel.text = permission.localizedDisplayName } return dataSource } @IBSegueAction func makeAppDetailCollectionViewController(_ coder: NSCoder, sender: Any?) -> UIViewController? { let appDetailViewController = AppDetailCollectionViewController(app: self.app, coder: coder) self.appDetailCollectionViewController = appDetailViewController return appDetailViewController } } private extension AppContentViewController { @objc func toggleCollapsingSection(_ sender: UIButton) { let indexPath: IndexPath switch sender { case self.descriptionTextView.moreButton: indexPath = IndexPath(row: Row.description.rawValue, section: 0) // Toggle the state for the text view self.descriptionTextView.isCollapsed.toggle() case self.versionDescriptionTextView.toggleButton: indexPath = IndexPath(row: Row.versionDescription.rawValue, section: 0) // Toggle the state for the markdown view self.versionDescriptionTextView.isCollapsed.toggle() default: return } // First apply the new state to the views switch Row.allCases[indexPath.row] { case .description: self.descriptionTextView.setNeedsLayout() self.descriptionTextView.layoutIfNeeded() case .versionDescription: self.versionDescriptionTextView.setNeedsLayout() self.versionDescriptionTextView.layoutIfNeeded() default: break } // Then reload the row with animation UIView.animate(withDuration: 0.25) { // Force table view to recalculate height self.tableView.beginUpdates() self.tableView.endUpdates() } } } extension AppContentViewController { override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { cell.tintColor = self.app.tintColor } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { switch Row.allCases[indexPath.row] { case .screenshots: guard !self.app.allScreenshots.isEmpty else { return 0.0 } return UITableView.automaticDimension case .permissions: guard !self.app.permissions.isEmpty else { return 0.0 } return UITableView.automaticDimension default: return super.tableView(tableView, heightForRowAt: indexPath) } } }