From aa52633491259874093c5b5a8aeba06d087bebaa Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Wed, 23 Oct 2019 13:32:17 -0700 Subject: [PATCH] Revises My Apps UI --- AltStore/Base.lproj/Main.storyboard | 134 +++++++------- AltStore/Components/AppBannerView.swift | 6 +- AltStore/Components/AppBannerView.xib | 110 ++++++++---- AltStore/Components/AppIconImageView.swift | 22 +-- AltStore/My Apps/MyAppsComponents.swift | 29 ++- AltStore/My Apps/MyAppsViewController.swift | 101 ++++++----- .../My Apps/UpdateCollectionViewCell.swift | 27 +-- AltStore/My Apps/UpdateCollectionViewCell.xib | 169 +++++++----------- 8 files changed, 314 insertions(+), 284 deletions(-) diff --git a/AltStore/Base.lproj/Main.storyboard b/AltStore/Base.lproj/Main.storyboard index c768a3b0..8e392304 100644 --- a/AltStore/Base.lproj/Main.storyboard +++ b/AltStore/Base.lproj/Main.storyboard @@ -685,104 +685,94 @@ World - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - + + + + - - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + @@ -830,7 +820,7 @@ World - + diff --git a/AltStore/Components/AppBannerView.swift b/AltStore/Components/AppBannerView.swift index 3187fbd9..e2eb257d 100644 --- a/AltStore/Components/AppBannerView.swift +++ b/AltStore/Components/AppBannerView.swift @@ -15,8 +15,12 @@ class AppBannerView: RSTNibView @IBOutlet var subtitleLabel: UILabel! @IBOutlet var iconImageView: AppIconImageView! @IBOutlet var button: PillButton! + @IBOutlet var buttonLabel: UILabel! @IBOutlet var betaBadgeView: UIView! + @IBOutlet var backgroundEffectView: UIVisualEffectView! + @IBOutlet private var vibrancyView: UIVisualEffectView! + override func tintColorDidChange() { super.tintColorDidChange() @@ -35,6 +39,6 @@ private extension AppBannerView self.subtitleLabel.textColor = self.tintColor self.button.tintColor = self.tintColor - self.backgroundColor = self.tintColor.withAlphaComponent(0.1) + self.backgroundEffectView.backgroundColor = self.tintColor } } diff --git a/AltStore/Components/AppBannerView.xib b/AltStore/Components/AppBannerView.xib index 932629ec..ad55a4aa 100644 --- a/AltStore/Components/AppBannerView.xib +++ b/AltStore/Components/AppBannerView.xib @@ -1,21 +1,22 @@ - - - - + + - + + + + @@ -23,6 +24,15 @@ + + + + + + + + + @@ -34,51 +44,91 @@ - + - - + + - + + + + + - + + + + + + + + + + + + + + + + + + + - + + + + + - - - - + + + + + + + + + diff --git a/AltStore/Components/AppIconImageView.swift b/AltStore/Components/AppIconImageView.swift index 2a8c6492..08371e8a 100644 --- a/AltStore/Components/AppIconImageView.swift +++ b/AltStore/Components/AppIconImageView.swift @@ -19,13 +19,16 @@ class AppIconImageView: UIImageView self.backgroundColor = .white - self.layer.borderWidth = 0.5 - self.layer.borderColor = self.tintColor.cgColor - - // Allows us to match system look for app icons. - if self.layer.responds(to: Selector(("continuousCorners"))) + if #available(iOS 13, *) { - self.layer.setValue(true, forKey: "continuousCorners") + self.layer.cornerCurve = .continuous + } + else + { + if self.layer.responds(to: Selector(("continuousCorners"))) + { + self.layer.setValue(true, forKey: "continuousCorners") + } } } @@ -37,11 +40,4 @@ class AppIconImageView: UIImageView let radius = self.bounds.height / 5 self.layer.cornerRadius = radius } - - override func tintColorDidChange() - { - super.tintColorDidChange() - - self.layer.borderColor = self.tintColor.cgColor - } } diff --git a/AltStore/My Apps/MyAppsComponents.swift b/AltStore/My Apps/MyAppsComponents.swift index 3733afce..74b2facd 100644 --- a/AltStore/My Apps/MyAppsComponents.swift +++ b/AltStore/My Apps/MyAppsComponents.swift @@ -10,11 +10,18 @@ import UIKit class InstalledAppCollectionViewCell: UICollectionViewCell { - @IBOutlet var appIconImageView: UIImageView! - @IBOutlet var nameLabel: UILabel! - @IBOutlet var developerLabel: UILabel! - @IBOutlet var refreshButton: PillButton! - @IBOutlet var betaBadgeView: UIImageView! + @IBOutlet var bannerView: AppBannerView! + + override func awakeFromNib() + { + super.awakeFromNib() + + self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.contentView.preservesSuperviewLayoutMargins = true + + self.bannerView.buttonLabel.text = NSLocalizedString("Expires in", comment: "") + self.bannerView.buttonLabel.isHidden = false + } } class InstalledAppsCollectionHeaderView: UICollectionReusableView @@ -23,6 +30,18 @@ class InstalledAppsCollectionHeaderView: UICollectionReusableView @IBOutlet var button: UIButton! } +class NoUpdatesCollectionViewCell: UICollectionViewCell +{ + @IBOutlet var blurView: UIVisualEffectView! + + override func awakeFromNib() + { + super.awakeFromNib() + + self.contentView.preservesSuperviewLayoutMargins = true + } +} + class UpdatesCollectionHeaderView: UICollectionReusableView { let button = PillButton(type: .system) diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index 605efa85..b2bc0616 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -74,7 +74,6 @@ class MyAppsViewController: UICollectionViewController self.collectionView.prefetchDataSource = self.dataSource self.prototypeUpdateCell = UpdateCollectionViewCell.instantiate(with: UpdateCollectionViewCell.nib!) - self.prototypeUpdateCell.translatesAutoresizingMaskIntoConstraints = false self.prototypeUpdateCell.contentView.translatesAutoresizingMaskIntoConstraints = false self.collectionView.register(UpdateCollectionViewCell.nib, forCellWithReuseIdentifier: "UpdateCell") @@ -156,9 +155,13 @@ private extension MyAppsViewController dynamicDataSource.numberOfItemsHandler = { _ in self.updatesDataSource.itemCount == 0 ? 1 : 0 } dynamicDataSource.cellIdentifierHandler = { _ in "NoUpdatesCell" } dynamicDataSource.cellConfigurationHandler = { (cell, _, indexPath) in - cell.layer.cornerRadius = 20 - cell.layer.masksToBounds = true - cell.contentView.backgroundColor = UIColor.altPrimary.withAlphaComponent(0.15) + let cell = cell as! NoUpdatesCollectionViewCell + cell.layoutMargins.left = self.view.layoutMargins.left + cell.layoutMargins.right = self.view.layoutMargins.right + + cell.blurView.layer.cornerRadius = 20 + cell.blurView.layer.masksToBounds = true + cell.blurView.backgroundColor = .altPrimary } return dynamicDataSource @@ -179,15 +182,19 @@ private extension MyAppsViewController guard let app = installedApp.storeApp else { return } let cell = cell as! UpdateCollectionViewCell - cell.tintColor = app.tintColor ?? .altPrimary - cell.nameLabel.text = app.name - cell.versionDescriptionTextView.text = app.versionDescription - cell.appIconImageView.image = nil - cell.appIconImageView.isIndicatingActivity = true - cell.betaBadgeView.isHidden = !app.isBeta + cell.layoutMargins.left = self.view.layoutMargins.left + cell.layoutMargins.right = self.view.layoutMargins.right - cell.updateButton.isIndicatingActivity = false - cell.updateButton.addTarget(self, action: #selector(MyAppsViewController.updateApp(_:)), for: .primaryActionTriggered) + cell.tintColor = app.tintColor ?? .altPrimary + cell.versionDescriptionTextView.text = app.versionDescription + + cell.bannerView.titleLabel.text = app.name + cell.bannerView.iconImageView.image = nil + cell.bannerView.iconImageView.isIndicatingActivity = true + cell.bannerView.betaBadgeView.isHidden = !app.isBeta + + cell.bannerView.button.isIndicatingActivity = false + cell.bannerView.button.addTarget(self, action: #selector(MyAppsViewController.updateApp(_:)), for: .primaryActionTriggered) if self.expandedAppUpdates.contains(app.bundleIdentifier) { @@ -201,9 +208,9 @@ private extension MyAppsViewController cell.versionDescriptionTextView.moreButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered) let progress = AppManager.shared.installationProgress(for: app) - cell.updateButton.progress = progress + cell.bannerView.button.progress = progress - cell.dateLabel.text = Date().relativeDateString(since: app.versionDate, dateFormatter: self.dateFormatter) + cell.bannerView.subtitleLabel.text = Date().relativeDateString(since: app.versionDate, dateFormatter: self.dateFormatter) cell.setNeedsLayout() } @@ -227,8 +234,8 @@ private extension MyAppsViewController } dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in let cell = cell as! UpdateCollectionViewCell - cell.appIconImageView.isIndicatingActivity = false - cell.appIconImageView.image = image + cell.bannerView.iconImageView.isIndicatingActivity = false + cell.bannerView.iconImageView.image = image if let error = error { @@ -254,12 +261,16 @@ private extension MyAppsViewController let tintColor = installedApp.storeApp?.tintColor ?? .altPrimary let cell = cell as! InstalledAppCollectionViewCell + cell.layoutMargins.left = self.view.layoutMargins.left + cell.layoutMargins.right = self.view.layoutMargins.right cell.tintColor = tintColor - cell.appIconImageView.isIndicatingActivity = true - cell.betaBadgeView.isHidden = !(installedApp.storeApp?.isBeta ?? false) - cell.refreshButton.isIndicatingActivity = false - cell.refreshButton.addTarget(self, action: #selector(MyAppsViewController.refreshApp(_:)), for: .primaryActionTriggered) + cell.bannerView.iconImageView.isIndicatingActivity = true + cell.bannerView.betaBadgeView.isHidden = !(installedApp.storeApp?.isBeta ?? false) + + cell.bannerView.button.isInverted = true + cell.bannerView.button.isIndicatingActivity = false + cell.bannerView.button.addTarget(self, action: #selector(MyAppsViewController.refreshApp(_:)), for: .primaryActionTriggered) let currentDate = Date() @@ -267,34 +278,34 @@ private extension MyAppsViewController if numberOfDays == 1 { - cell.refreshButton.setTitle(NSLocalizedString("1 DAY", comment: ""), for: .normal) + cell.bannerView.button.setTitle(NSLocalizedString("1 DAY", comment: ""), for: .normal) } else { - cell.refreshButton.setTitle(String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays)), for: .normal) + cell.bannerView.button.setTitle(String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays)), for: .normal) } - cell.nameLabel.text = installedApp.name - cell.developerLabel.text = installedApp.storeApp?.developerName ?? NSLocalizedString("Sideloaded", comment: "") + cell.bannerView.titleLabel.text = installedApp.name + cell.bannerView.subtitleLabel.text = installedApp.storeApp?.developerName ?? NSLocalizedString("Sideloaded", comment: "") // Make sure refresh button is correct size. cell.layoutIfNeeded() switch numberOfDays { - case 2...3: cell.refreshButton.tintColor = .refreshOrange - case 4...5: cell.refreshButton.tintColor = .refreshYellow - case 6...: cell.refreshButton.tintColor = .refreshGreen - default: cell.refreshButton.tintColor = .refreshRed + case 2...3: cell.bannerView.button.tintColor = .refreshOrange + case 4...5: cell.bannerView.button.tintColor = .refreshYellow + case 6...: cell.bannerView.button.tintColor = .refreshGreen + default: cell.bannerView.button.tintColor = .refreshRed } if let refreshGroup = self.refreshGroup, let progress = refreshGroup.progress(for: installedApp), progress.fractionCompleted < 1.0 { - cell.refreshButton.progress = progress + cell.bannerView.button.progress = progress } else { - cell.refreshButton.progress = nil + cell.bannerView.button.progress = nil } } dataSource.prefetchHandler = { (item, indexPath, completion) in @@ -312,8 +323,8 @@ private extension MyAppsViewController } dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in let cell = cell as! InstalledAppCollectionViewCell - cell.appIconImageView.image = image - cell.appIconImageView.isIndicatingActivity = false + cell.bannerView.iconImageView.image = image + cell.bannerView.iconImageView.isIndicatingActivity = false } return dataSource @@ -813,14 +824,11 @@ extension MyAppsViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - let padding = 30 as CGFloat - let width = collectionView.bounds.width - padding - let section = Section.allCases[indexPath.section] switch section { case .noUpdates: - let size = CGSize(width: width, height: 44) + let size = CGSize(width: collectionView.bounds.width, height: 44) return size case .updates: @@ -831,7 +839,10 @@ extension MyAppsViewController: UICollectionViewDelegateFlowLayout return previousHeight } - let widthConstraint = self.prototypeUpdateCell.contentView.widthAnchor.constraint(equalToConstant: width) + // Manually change cell's width to prevent conflicting with UIView-Encapsulated-Layout-Width constraints. + self.prototypeUpdateCell.frame.size.width = collectionView.bounds.width + + let widthConstraint = self.prototypeUpdateCell.contentView.widthAnchor.constraint(equalToConstant: collectionView.bounds.width) NSLayoutConstraint.activate([widthConstraint]) defer { NSLayoutConstraint.deactivate([widthConstraint]) } @@ -842,7 +853,7 @@ extension MyAppsViewController: UICollectionViewDelegateFlowLayout return size case .installedApps: - return CGSize(width: collectionView.bounds.width, height: 60) + return CGSize(width: collectionView.bounds.width, height: 88) } } @@ -860,20 +871,14 @@ extension MyAppsViewController: UICollectionViewDelegateFlowLayout } } - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets + func collectionView(_ myCV: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { let section = Section.allCases[section] switch section { - case .noUpdates: - guard self.updatesDataSource.itemCount == 0 else { return .zero } - return UIEdgeInsets(top: 12, left: 15, bottom: 20, right: 15) - - case .updates: - guard self.updatesDataSource.itemCount > 0 else { return .zero } - return UIEdgeInsets(top: 12, left: 15, bottom: 20, right: 15) - - case .installedApps: return UIEdgeInsets(top: 12, left: 0, bottom: 20, right: 0) + case .noUpdates where self.updatesDataSource.itemCount != 0: return .zero + case .updates where self.updatesDataSource.itemCount == 0: return .zero + default: return UIEdgeInsets(top: 12, left: 0, bottom: 20, right: 0) } } } diff --git a/AltStore/My Apps/UpdateCollectionViewCell.swift b/AltStore/My Apps/UpdateCollectionViewCell.swift index 04cff8e0..5dbe41c1 100644 --- a/AltStore/My Apps/UpdateCollectionViewCell.swift +++ b/AltStore/My Apps/UpdateCollectionViewCell.swift @@ -25,20 +25,25 @@ extension UpdateCollectionViewCell } } - @IBOutlet var appIconImageView: UIImageView! - @IBOutlet var nameLabel: UILabel! - @IBOutlet var dateLabel: UILabel! - @IBOutlet var updateButton: PillButton! + @IBOutlet var bannerView: AppBannerView! @IBOutlet var versionDescriptionTitleLabel: UILabel! @IBOutlet var versionDescriptionTextView: CollapsingTextView! - @IBOutlet var betaBadgeView: UIImageView! + + @IBOutlet private var blurView: UIVisualEffectView! override func awakeFromNib() { super.awakeFromNib() - self.contentView.layer.cornerRadius = 20 - self.contentView.layer.masksToBounds = true + // Prevent temporary unsatisfiable constraint errors due to UIView-Encapsulated-Layout constraints. + self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.contentView.preservesSuperviewLayoutMargins = true + + self.bannerView.backgroundEffectView.isHidden = true + self.bannerView.button.setTitle(NSLocalizedString("UPDATE", comment: ""), for: .normal) + + self.blurView.layer.cornerRadius = 20 + self.blurView.layer.masksToBounds = true self.update() } @@ -87,11 +92,11 @@ private extension UpdateCollectionViewCell } self.versionDescriptionTitleLabel.textColor = self.tintColor - self.contentView.backgroundColor = self.tintColor.withAlphaComponent(0.1) + self.blurView.backgroundColor = self.tintColor - self.updateButton.setTitleColor(self.tintColor, for: .normal) - self.updateButton.backgroundColor = self.tintColor.withAlphaComponent(0.15) - self.updateButton.progressTintColor = self.tintColor + self.bannerView.button.isInverted = true + self.bannerView.button.tintColor = self.tintColor + self.bannerView.button.progressTintColor = self.tintColor self.setNeedsLayout() self.layoutIfNeeded() diff --git a/AltStore/My Apps/UpdateCollectionViewCell.xib b/AltStore/My Apps/UpdateCollectionViewCell.xib index 4b928242..a35dd0a7 100644 --- a/AltStore/My Apps/UpdateCollectionViewCell.xib +++ b/AltStore/My Apps/UpdateCollectionViewCell.xib @@ -1,113 +1,81 @@ - - - - + + - - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + - @@ -115,23 +83,16 @@ - - + + - - - - - - + + - - -