[AltStore] Revises AppViewController UI

- Fades in navigation bar as user scrolls down
- Displays version number, version date, and app size
This commit is contained in:
Riley Testut
2019-07-29 16:03:22 -07:00
parent 083f28896e
commit 52cb01c6c7
7 changed files with 332 additions and 122 deletions

View File

@@ -168,6 +168,7 @@
BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; }; BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; };
BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; }; BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; };
BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */; }; BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */; };
BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */; };
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */; }; BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */; };
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; }; BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; };
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */; }; BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */; };
@@ -416,6 +417,7 @@
BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; }; BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; };
BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; }; BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; };
BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsComponents.swift; sourceTree = "<group>"; }; BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsComponents.swift; sourceTree = "<group>"; };
BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+RelativeDate.swift"; sourceTree = "<group>"; };
BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; }; BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = "<group>"; }; BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = "<group>"; };
BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignAppOperation.swift; sourceTree = "<group>"; }; BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignAppOperation.swift; sourceTree = "<group>"; };
@@ -855,6 +857,7 @@
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */, BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */,
BF43002F22A71C960051E2BC /* UserDefaults+AltStore.swift */, BF43002F22A71C960051E2BC /* UserDefaults+AltStore.swift */,
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */, BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */,
BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1311,6 +1314,7 @@
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */, BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */, BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */,
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */, BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */,
BF770E5622BC3C03002A40FE /* Server.swift in Sources */, BF770E5622BC3C03002A40FE /* Server.swift in Sources */,
BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */, BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */,
); );

View File

@@ -29,9 +29,24 @@ class AppContentViewController: UITableViewController
private lazy var screenshotsDataSource = self.makeScreenshotsDataSource() private lazy var screenshotsDataSource = self.makeScreenshotsDataSource()
private lazy var permissionsDataSource = self.makePermissionsDataSource() private lazy var permissionsDataSource = self.makePermissionsDataSource()
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 subtitleLabel: UILabel!
@IBOutlet private var descriptionTextView: CollapsingTextView! @IBOutlet private var descriptionTextView: CollapsingTextView!
@IBOutlet private var versionDescriptionTextView: CollapsingTextView! @IBOutlet private var versionDescriptionTextView: CollapsingTextView!
@IBOutlet private var versionLabel: UILabel!
@IBOutlet private var versionDateLabel: UILabel!
@IBOutlet private var sizeLabel: UILabel!
@IBOutlet private var screenshotsCollectionView: UICollectionView! @IBOutlet private var screenshotsCollectionView: UICollectionView!
@IBOutlet private var permissionsCollectionView: UICollectionView! @IBOutlet private var permissionsCollectionView: UICollectionView!
@@ -63,6 +78,9 @@ class AppContentViewController: UITableViewController
self.subtitleLabel.text = self.app.subtitle self.subtitleLabel.text = self.app.subtitle
self.descriptionTextView.text = self.app.localizedDescription self.descriptionTextView.text = self.app.localizedDescription
self.versionDescriptionTextView.text = self.app.versionDescription self.versionDescriptionTextView.text = self.app.versionDescription
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), self.app.version)
self.versionDateLabel.text = Date().relativeDateString(since: self.app.versionDate, dateFormatter: self.dateFormatter)
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: Int64(self.app.size))
self.descriptionTextView.maximumNumberOfLines = 5 self.descriptionTextView.maximumNumberOfLines = 5
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered) self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)

View File

@@ -18,6 +18,7 @@ class AppViewController: UIViewController
private var contentViewControllerShadowView: UIView! private var contentViewControllerShadowView: UIView!
private var blurAnimator: UIViewPropertyAnimator? private var blurAnimator: UIViewPropertyAnimator?
private var navigationBarAnimator: UIViewPropertyAnimator?
private var contentSizeObservation: NSKeyValueObservation? private var contentSizeObservation: NSKeyValueObservation?
@@ -38,13 +39,21 @@ class AppViewController: UIViewController
@IBOutlet private var backgroundAppIconImageView: UIImageView! @IBOutlet private var backgroundAppIconImageView: UIImageView!
@IBOutlet private var backgroundBlurView: UIVisualEffectView! @IBOutlet private var backgroundBlurView: UIVisualEffectView!
private var _isEnteringForeground = false @IBOutlet private var navigationBarTitleView: UIView!
@IBOutlet private var navigationBarDownloadButton: PillButton!
@IBOutlet private var navigationBarAppIconImageView: UIImageView!
@IBOutlet private var navigationBarAppNameLabel: UILabel!
private var _shouldResetLayout = false
private var _backgroundBlurEffect: UIBlurEffect? private var _backgroundBlurEffect: UIBlurEffect?
private var _backgroundBlurTintColor: UIColor? private var _backgroundBlurTintColor: UIColor?
override func viewDidLoad() override func viewDidLoad()
{ {
super.viewDidLoad() super.viewDidLoad()
self.navigationBarTitleView.sizeToFit()
self.navigationItem.titleView = self.navigationBarTitleView
self.contentViewControllerShadowView = UIView() self.contentViewControllerShadowView = UIView()
self.contentViewControllerShadowView.backgroundColor = .white self.contentViewControllerShadowView.backgroundColor = .white
@@ -75,14 +84,21 @@ class AppViewController: UIViewController
self.developerLabel.text = self.app.developerName self.developerLabel.text = self.app.developerName
self.developerLabel.textColor = self.app.tintColor self.developerLabel.textColor = self.app.tintColor
self.appIconImageView.image = UIImage(named: self.app.iconName) self.appIconImageView.image = UIImage(named: self.app.iconName)
self.appIconImageView.tintColor = self.app.tintColor
self.downloadButton.tintColor = self.app.tintColor self.downloadButton.tintColor = self.app.tintColor
self.backgroundAppIconImageView.image = UIImage(named: self.app.iconName) self.backgroundAppIconImageView.image = UIImage(named: self.app.iconName)
self.backButtonContainerView.tintColor = self.app.tintColor self.backButtonContainerView.tintColor = self.app.tintColor
self.contentSizeObservation = self.contentViewController.tableView.observe(\.contentSize) { (tableView, change) in self.navigationController?.navigationBar.tintColor = self.app.tintColor
self.view.setNeedsLayout() self.navigationBarDownloadButton.tintColor = self.app.tintColor
self.view.layoutIfNeeded() self.navigationBarAppNameLabel.text = self.app.name
self.navigationBarAppIconImageView.image = UIImage(named: self.app.iconName)
self.navigationBarAppIconImageView.tintColor = self.app.tintColor
self.contentSizeObservation = self.contentViewController.tableView.observe(\.contentSize) { [weak self] (tableView, change) in
self?.view.setNeedsLayout()
self?.view.layoutIfNeeded()
} }
self.update() self.update()
@@ -113,7 +129,9 @@ class AppViewController: UIViewController
{ {
super.viewDidAppear(animated) super.viewDidAppear(animated)
self.hideNavigationBar() self._shouldResetLayout = true
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
} }
override func viewWillDisappear(_ animated: Bool) override func viewWillDisappear(_ animated: Bool)
@@ -130,17 +148,23 @@ class AppViewController: UIViewController
self.transitionCoordinator?.animate(alongsideTransition: { (context) in self.transitionCoordinator?.animate(alongsideTransition: { (context) in
self.showNavigationBar(for: navigationController) self.showNavigationBar(for: navigationController)
}, completion: { (context) in }, completion: { (context) in
if context.isCancelled if !context.isCancelled
{
self.hideNavigationBar(for: navigationController)
}
else
{ {
self.showNavigationBar(for: navigationController) self.showNavigationBar(for: navigationController)
} }
}) })
} }
override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
if self.navigationController == nil
{
self.resetNavigationBarAnimation()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{ {
guard segue.identifier == "embedAppContentViewController" else { return } guard segue.identifier == "embedAppContentViewController" else { return }
@@ -153,9 +177,9 @@ class AppViewController: UIViewController
{ {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
if self._isEnteringForeground if self._shouldResetLayout
{ {
// Returning from background messes up some of our UI, so reset affected components now. // Various events can cause UI to mess up, so reset affected components now.
if self.navigationController?.topViewController == self if self.navigationController?.topViewController == self
{ {
@@ -164,16 +188,20 @@ class AppViewController: UIViewController
self.prepareBlur() self.prepareBlur()
self._isEnteringForeground = false // Reset navigation bar animation, and create a new one later in this method if necessary.
self.resetNavigationBarAnimation()
self._shouldResetLayout = false
} }
let statusBarHeight = UIApplication.shared.statusBarFrame.height let statusBarHeight = UIApplication.shared.statusBarFrame.height
let cornerRadius = self.contentViewControllerShadowView.layer.cornerRadius
let inset = 12 as CGFloat let inset = 12 as CGFloat
let padding = 20 as CGFloat let padding = 20 as CGFloat
let backButtonSize = self.backButton.sizeThatFits(CGSize(width: 1000, height: 1000)) let backButtonSize = self.backButton.sizeThatFits(CGSize(width: 1000, height: 1000))
let backButtonFrame = CGRect(x: inset, y: statusBarHeight, var backButtonFrame = CGRect(x: inset, y: statusBarHeight,
width: backButtonSize.width + 20, height: backButtonSize.height + 20) width: backButtonSize.width + 20, height: backButtonSize.height + 20)
var headerFrame = CGRect(x: inset, y: 0, width: self.view.bounds.width - inset * 2, height: self.headerView.bounds.height) var headerFrame = CGRect(x: inset, y: 0, width: self.view.bounds.width - inset * 2, height: self.headerView.bounds.height)
@@ -183,58 +211,80 @@ class AppViewController: UIViewController
let minimumHeaderY = backButtonFrame.maxY + 8 let minimumHeaderY = backButtonFrame.maxY + 8
let minimumContentY = minimumHeaderY + headerFrame.height + padding let minimumContentY = minimumHeaderY + headerFrame.height + padding
let maximumContentY = self.view.bounds.width * 0.75 let maximumContentY = self.view.bounds.width * 0.667
// A full blur is too much, so we reduce the visible blur by 0.3, resulting in 70% blur. // A full blur is too much, so we reduce the visible blur by 0.3, resulting in 70% blur.
let minimumBlurFraction = 0.3 as CGFloat let minimumBlurFraction = 0.3 as CGFloat
let difference = (maximumContentY - minimumContentY) contentFrame.origin.y = maximumContentY - self.scrollView.contentOffset.y
headerFrame.origin.y = contentFrame.origin.y - padding - headerFrame.height
if self.scrollView.contentOffset.y > difference // Stretch the app icon image to fill additional vertical space if necessary.
let height = max(contentFrame.origin.y + cornerRadius * 2, backgroundIconFrame.height)
backgroundIconFrame.size.height = height
let blurThreshold = 0 as CGFloat
if self.scrollView.contentOffset.y < blurThreshold
{ {
// Full screen // Determine how much to lessen blur by.
headerFrame.origin.y = minimumHeaderY let range = 75 as CGFloat
contentFrame.origin.y = minimumContentY let difference = -self.scrollView.contentOffset.y
backgroundIconFrame.origin.y = 0
self.contentViewController.tableView.contentOffset.y = self.scrollView.contentOffset.y - difference let fraction = min(difference, range) / range
let fractionComplete = (fraction * (1.0 - minimumBlurFraction)) + minimumBlurFraction
self.blurAnimator?.fractionComplete = fractionComplete
} }
else else
{ {
// Partial screen // Set blur to default.
contentFrame.origin.y = maximumContentY - self.scrollView.contentOffset.y self.blurAnimator?.fractionComplete = minimumBlurFraction
headerFrame.origin.y = contentFrame.origin.y - padding - headerFrame.height
let cornerRadius = self.contentViewControllerShadowView.layer.cornerRadius
// Stretch the app icon image to fill additional vertical space if necessary.
let height = max(contentFrame.origin.y + cornerRadius * 2, backgroundIconFrame.height)
backgroundIconFrame.size.height = height
// Keep content table view's content offset at the top.
self.contentViewController.tableView.contentOffset.y = 0
if self.scrollView.contentOffset.y < 0
{
// Determine how much to lessen blur by.
let range = 75 as CGFloat
let fraction = min(-self.scrollView.contentOffset.y, range) / range
let fractionComplete = (fraction * (1.0 - minimumBlurFraction)) + minimumBlurFraction
self.blurAnimator?.fractionComplete = fractionComplete
}
else
{
// Set blur to default.
self.blurAnimator?.fractionComplete = minimumBlurFraction
}
} }
// Animate navigation bar.
let showNavigationBarThreshold = (maximumContentY - minimumContentY) + backButtonFrame.origin.y
if self.scrollView.contentOffset.y > showNavigationBarThreshold
{
if self.navigationBarAnimator == nil
{
self.prepareNavigationBarAnimation()
}
let difference = self.scrollView.contentOffset.y - showNavigationBarThreshold
let range = (headerFrame.height + padding) - (self.navigationController?.navigationBar.bounds.height ?? self.view.safeAreaInsets.top)
let fractionComplete = min(difference, range) / range
self.navigationBarAnimator?.fractionComplete = fractionComplete
}
else
{
self.resetNavigationBarAnimation()
}
let beginMovingBackButtonThreshold = (maximumContentY - minimumContentY)
if self.scrollView.contentOffset.y > beginMovingBackButtonThreshold
{
let difference = self.scrollView.contentOffset.y - beginMovingBackButtonThreshold
backButtonFrame.origin.y -= difference
}
let pinContentToTopThreshold = maximumContentY
if self.scrollView.contentOffset.y > pinContentToTopThreshold
{
contentFrame.origin.y = 0
backgroundIconFrame.origin.y = 0
let difference = self.scrollView.contentOffset.y - pinContentToTopThreshold
self.contentViewController.tableView.contentOffset.y = difference
}
else
{
// Keep content table view's content offset at the top.
self.contentViewController.tableView.contentOffset.y = 0
}
// Keep background app icon centered in gap between top of content and top of screen. // Keep background app icon centered in gap between top of content and top of screen.
backgroundIconFrame.origin.y = (contentFrame.origin.y / 2) - backgroundIconFrame.height / 2 backgroundIconFrame.origin.y = (contentFrame.origin.y / 2) - backgroundIconFrame.height / 2
@@ -256,7 +306,7 @@ class AppViewController: UIViewController
let contentOffset = self.scrollView.contentOffset let contentOffset = self.scrollView.contentOffset
var contentSize = self.contentViewController.tableView.contentSize var contentSize = self.contentViewController.tableView.contentSize
contentSize.height += minimumContentY + self.view.safeAreaInsets.bottom + self.contentViewController.tableView.contentInset.bottom contentSize.height += maximumContentY
self.scrollView.contentSize = contentSize self.scrollView.contentSize = contentSize
self.scrollView.contentOffset = contentOffset self.scrollView.contentOffset = contentOffset
@@ -265,6 +315,7 @@ class AppViewController: UIViewController
deinit deinit
{ {
self.blurAnimator?.stopAnimation(true) self.blurAnimator?.stopAnimation(true)
self.navigationBarAnimator?.stopAnimation(true)
} }
} }
@@ -272,21 +323,29 @@ private extension AppViewController
{ {
func update() func update()
{ {
self.downloadButton.isIndicatingActivity = false for button in [self.downloadButton!, self.navigationBarDownloadButton!]
if self.app.installedApp == nil
{ {
self.downloadButton.setTitle(NSLocalizedString("FREE", comment: ""), for: .normal) button.tintColor = self.app.tintColor
self.downloadButton.isInverted = false button.isIndicatingActivity = false
}
else if self.app.installedApp == nil
{ {
self.downloadButton.setTitle(NSLocalizedString("OPEN", comment: ""), for: .normal) button.setTitle(NSLocalizedString("FREE", comment: ""), for: .normal)
self.downloadButton.isInverted = true button.isInverted = false
}
else
{
button.setTitle(NSLocalizedString("OPEN", comment: ""), for: .normal)
button.isInverted = true
}
let progress = AppManager.shared.installationProgress(for: self.app)
button.progress = progress
} }
let progress = AppManager.shared.installationProgress(for: self.app) let barButtonItem = self.navigationItem.rightBarButtonItem
self.downloadButton.progress = progress self.navigationItem.rightBarButtonItem = nil
self.navigationItem.rightBarButtonItem = barButtonItem
} }
func showNavigationBar(for navigationController: UINavigationController? = nil) func showNavigationBar(for navigationController: UINavigationController? = nil)
@@ -294,7 +353,8 @@ private extension AppViewController
let navigationController = navigationController ?? self.navigationController let navigationController = navigationController ?? self.navigationController
navigationController?.navigationBar.barStyle = .default navigationController?.navigationBar.barStyle = .default
navigationController?.navigationBar.alpha = 1.0 navigationController?.navigationBar.alpha = 1.0
navigationController?.navigationBar.setBackgroundImage(nil, for: .default) navigationController?.navigationBar.barTintColor = .white
navigationController?.navigationBar.tintColor = .altGreen
} }
func hideNavigationBar(for navigationController: UINavigationController? = nil) func hideNavigationBar(for navigationController: UINavigationController? = nil)
@@ -302,7 +362,7 @@ private extension AppViewController
let navigationController = navigationController ?? self.navigationController let navigationController = navigationController ?? self.navigationController
navigationController?.navigationBar.barStyle = .black navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.alpha = 0.0 navigationController?.navigationBar.alpha = 0.0
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) navigationController?.navigationBar.barTintColor = .white
} }
func prepareBlur() func prepareBlur()
@@ -315,14 +375,41 @@ private extension AppViewController
self.backgroundBlurView.effect = self._backgroundBlurEffect self.backgroundBlurView.effect = self._backgroundBlurEffect
self.backgroundBlurView.contentView.backgroundColor = self._backgroundBlurTintColor self.backgroundBlurView.contentView.backgroundColor = self._backgroundBlurTintColor
self.blurAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { self.blurAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { [weak self] in
self.backgroundBlurView.effect = nil self?.backgroundBlurView.effect = nil
self.backgroundBlurView.contentView.backgroundColor = .clear self?.backgroundBlurView.contentView.backgroundColor = .clear
} }
self.blurAnimator?.startAnimation() self.blurAnimator?.startAnimation()
self.blurAnimator?.pauseAnimation() self.blurAnimator?.pauseAnimation()
} }
func prepareNavigationBarAnimation()
{
self.resetNavigationBarAnimation()
self.navigationBarAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { [weak self] in
self?.showNavigationBar()
self?.navigationController?.navigationBar.tintColor = self?.app.tintColor
self?.navigationController?.navigationBar.barTintColor = nil
self?.contentViewController.view.layer.cornerRadius = 0
}
self.navigationBarAnimator?.startAnimation()
self.navigationBarAnimator?.pauseAnimation()
self.update()
}
func resetNavigationBarAnimation()
{
self.navigationBarAnimator?.stopAnimation(true)
self.navigationBarAnimator = nil
self.hideNavigationBar()
self.navigationController?.navigationBar.barTintColor = .white
self.contentViewController.view.layer.cornerRadius = self.contentViewControllerShadowView.layer.cornerRadius
}
} }
extension AppViewController extension AppViewController
@@ -391,7 +478,7 @@ private extension AppViewController
{ {
guard let navigationController = self.navigationController, navigationController.topViewController == self else { return } guard let navigationController = self.navigationController, navigationController.topViewController == self else { return }
self._isEnteringForeground = true self._shouldResetLayout = true
self.view.setNeedsLayout() self.view.setNeedsLayout()
} }
} }

View File

@@ -185,6 +185,25 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="Bql-t3-Ndi">
<rect key="frame" x="47" y="238" width="85" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="j1W-Jn-HFI" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="35" height="35"/>
<constraints>
<constraint firstAttribute="width" constant="35" id="aMS-N7-WYn"/>
<constraint firstAttribute="height" constant="35" id="psb-Ro-Js4"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delta" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DTD-1Y-76c">
<rect key="frame" x="43" y="0.0" width="42" height="35"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CUB-SN-zdM"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CUB-SN-zdM">
<rect key="frame" x="67" y="113" width="240" height="128"/> <rect key="frame" x="67" y="113" width="240" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
@@ -217,7 +236,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" alignment="center" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="LZw-eU-5SO" userLabel="App Info"> <stackView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" insetsLayoutMarginsFromSafeArea="NO" alignment="center" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="LZw-eU-5SO" userLabel="App Info">
<rect key="frame" x="0.0" y="0.0" width="270" height="93"/> <rect key="frame" x="0.0" y="0.0" width="273" height="93"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
<subviews> <subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="3Ey-6S-HJx" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="3Ey-6S-HJx" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
@@ -228,16 +247,16 @@
</constraints> </constraints>
</imageView> </imageView>
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="100" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="bR7-SO-m8f"> <stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="100" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="bR7-SO-m8f">
<rect key="frame" x="90" y="26.5" width="85" height="40.5"/> <rect key="frame" x="90" y="26.5" width="88" height="40.5"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="App Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dNE-IO-y3o"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="App Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dNE-IO-y3o">
<rect key="frame" x="0.0" y="0.0" width="85" height="21.5"/> <rect key="frame" x="0.0" y="0.0" width="88" height="21.5"/>
<fontDescription key="fontDescription" type="system" pointSize="18"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Developer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NKT-el-rRF"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Developer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NKT-el-rRF">
<rect key="frame" x="0.0" y="23.5" width="85" height="17"/> <rect key="frame" x="0.0" y="23.5" width="88" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@@ -245,7 +264,7 @@
</subviews> </subviews>
</stackView> </stackView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mgB-Gs-bik" customClass="PillButton" customModule="AltStore" customModuleProvider="target"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mgB-Gs-bik" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="186" y="31" width="72" height="31"/> <rect key="frame" x="189" y="31" width="72" height="31"/>
<color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="72" id="j44-T1-0dc"/> <constraint firstAttribute="width" constant="72" id="j44-T1-0dc"/>
@@ -272,16 +291,16 @@
</connections> </connections>
</containerView> </containerView>
<visualEffectView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tUK-0J-07U"> <visualEffectView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tUK-0J-07U">
<rect key="frame" x="58" y="117" width="58" height="32"/> <rect key="frame" x="58" y="117" width="18" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" id="yyn-wP-xk4"> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" insetsLayoutMarginsFromSafeArea="NO" id="yyn-wP-xk4">
<rect key="frame" x="0.0" y="0.0" width="58" height="32"/> <rect key="frame" x="0.0" y="0.0" width="18" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mkD-3C-WMV"> <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mkD-3C-WMV">
<rect key="frame" x="3" y="7" width="52" height="18"/> <rect key="frame" x="0.0" y="0.0" width="18" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<state key="normal" title="Back" image="Back"/> <state key="normal" image="Back"/>
<connections> <connections>
<action selector="popViewController:" destination="0V6-N4-hTO" eventType="primaryActionTriggered" id="F6Z-xz-qCk"/> <action selector="popViewController:" destination="0V6-N4-hTO" eventType="primaryActionTriggered" id="F6Z-xz-qCk"/>
</connections> </connections>
@@ -306,7 +325,24 @@
</constraints> </constraints>
<viewLayoutGuide key="safeArea" id="wiR-52-nwg"/> <viewLayoutGuide key="safeArea" id="wiR-52-nwg"/>
</view> </view>
<navigationItem key="navigationItem" largeTitleDisplayMode="never" id="maq-gT-QcS"/> <navigationItem key="navigationItem" largeTitleDisplayMode="never" id="maq-gT-QcS">
<barButtonItem key="rightBarButtonItem" style="plain" id="FLf-DS-F77">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="grk-xM-YWA" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="287" y="6.5" width="72" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="31" id="S2r-fI-PQB"/>
<constraint firstAttribute="width" constant="72" id="Xtq-UG-h3b"/>
</constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
<state key="normal" title="FREE"/>
<connections>
<action selector="performAppAction:" destination="0V6-N4-hTO" eventType="primaryActionTriggered" id="b2l-CA-mbO"/>
</connections>
</button>
</barButtonItem>
</navigationItem>
<connections> <connections>
<outlet property="appIconImageView" destination="3Ey-6S-HJx" id="5FB-mn-E29"/> <outlet property="appIconImageView" destination="3Ey-6S-HJx" id="5FB-mn-E29"/>
<outlet property="backButton" destination="mkD-3C-WMV" id="3m8-P7-yvT"/> <outlet property="backButton" destination="mkD-3C-WMV" id="3m8-P7-yvT"/>
@@ -319,6 +355,10 @@
<outlet property="headerContentView" destination="LZw-eU-5SO" id="hk1-xG-2kJ"/> <outlet property="headerContentView" destination="LZw-eU-5SO" id="hk1-xG-2kJ"/>
<outlet property="headerView" destination="mgO-eN-SxQ" id="iIi-D7-XRt"/> <outlet property="headerView" destination="mgO-eN-SxQ" id="iIi-D7-XRt"/>
<outlet property="nameLabel" destination="dNE-IO-y3o" id="tp1-IT-ByH"/> <outlet property="nameLabel" destination="dNE-IO-y3o" id="tp1-IT-ByH"/>
<outlet property="navigationBarAppIconImageView" destination="j1W-Jn-HFI" id="2YU-ka-w9R"/>
<outlet property="navigationBarAppNameLabel" destination="DTD-1Y-76c" id="z9z-pp-dC4"/>
<outlet property="navigationBarDownloadButton" destination="grk-xM-YWA" id="Yrg-S0-tIM"/>
<outlet property="navigationBarTitleView" destination="Bql-t3-Ndi" id="aY1-Mt-1rl"/>
<outlet property="scrollView" destination="Ci9-Iw-aR2" id="6XJ-gn-ByV"/> <outlet property="scrollView" destination="Ci9-Iw-aR2" id="6XJ-gn-ByV"/>
</connections> </connections>
</viewController> </viewController>
@@ -330,7 +370,7 @@
<scene sceneID="CgX-7h-sRI"> <scene sceneID="CgX-7h-sRI">
<objects> <objects>
<tableViewController id="kBq-V8-3XC" customClass="AppContentViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="kBq-V8-3XC" customClass="AppContentViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="w5c-Q3-FcU"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" contentInsetAdjustmentBehavior="never" dataMode="static" style="plain" separatorStyle="none" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" contentViewInsetsToSafeArea="NO" id="w5c-Q3-FcU">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -346,7 +386,7 @@
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Classic games in your pocket" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BsL-O2-UjD"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Classic games in your pocket" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BsL-O2-UjD">
<rect key="frame" x="20" y="20" width="335" height="17.5"/> <rect key="frame" x="20" y="20" width="335" height="17.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/> <fontDescription key="fontDescription" type="system" pointSize="14"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
@@ -400,12 +440,12 @@
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="EL5-UC-RIw" customClass="AppContentTableViewCell" customModule="AltStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="EL5-UC-RIw" customClass="AppContentTableViewCell" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="101" width="375" height="44"/> <rect key="frame" x="0.0" y="101" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EL5-UC-RIw" id="D1G-nK-G0Z"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EL5-UC-RIw" id="D1G-nK-G0Z">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="20" y="20" width="335" height="4"/> <rect key="frame" x="20" y="20" width="335" height="34"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
@@ -413,7 +453,7 @@
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="Pyt-8D-BZA" firstAttribute="top" secondItem="D1G-nK-G0Z" secondAttribute="top" constant="20" id="Lm9-lx-kJ8"/> <constraint firstItem="Pyt-8D-BZA" firstAttribute="top" secondItem="D1G-nK-G0Z" secondAttribute="top" constant="20" id="Lm9-lx-kJ8"/>
<constraint firstAttribute="bottom" secondItem="Pyt-8D-BZA" secondAttribute="bottom" constant="20" id="TSS-Au-gYx"/> <constraint firstAttribute="bottom" secondItem="Pyt-8D-BZA" secondAttribute="bottom" constant="44" id="TSS-Au-gYx"/>
<constraint firstItem="Pyt-8D-BZA" firstAttribute="leading" secondItem="D1G-nK-G0Z" secondAttribute="leading" constant="20" id="UlS-ct-L9Y"/> <constraint firstItem="Pyt-8D-BZA" firstAttribute="leading" secondItem="D1G-nK-G0Z" secondAttribute="leading" constant="20" id="UlS-ct-L9Y"/>
<constraint firstAttribute="trailing" secondItem="Pyt-8D-BZA" secondAttribute="trailing" constant="20" id="Wq4-Ql-wvN"/> <constraint firstAttribute="trailing" secondItem="Pyt-8D-BZA" secondAttribute="trailing" constant="20" id="Wq4-Ql-wvN"/>
</constraints> </constraints>
@@ -427,23 +467,56 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="n9R-39-Glq"> <stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" distribution="equalSpacing" spacing="12" translatesAutoresizingMaskIntoConstraints="NO" id="n9R-39-Glq">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/> <rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="What's New" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="obM-TM-y2E"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="y3w-4S-e64">
<rect key="frame" x="20" y="0.0" width="335" height="13"/> <rect key="frame" x="20" y="0.0" width="335" height="4"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/> <subviews>
<nil key="textColor"/> <stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="PFD-gZ-77F">
<nil key="highlightedColor"/> <rect key="frame" x="0.0" y="0.0" width="335" height="0.0"/>
</label> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="What's New" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="obM-TM-y2E">
<rect key="frame" x="0.0" y="0.0" width="123.5" height="0.0"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="22"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="2w ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wGD-mS-8fO">
<rect key="frame" x="285" y="0.0" width="50" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="ewH-gi-pyW">
<rect key="frame" x="0.0" y="4" width="335" height="0.0"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Version 4.4.2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7E0-TV-G4l">
<rect key="frame" x="0.0" y="0.0" width="84.5" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="50.4 MB" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DgM-bD-bBY">
<rect key="frame" x="280.5" y="0.0" width="54.5" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
</stackView>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
<rect key="frame" x="20" y="24" width="335" height="0.0"/> <rect key="frame" x="20" y="16" width="335" height="0.0"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>
</subviews> </subviews>
<edgeInsets key="layoutMargins" top="0.0" left="20" bottom="20" right="20"/> <edgeInsets key="layoutMargins" top="0.0" left="20" bottom="44" right="20"/>
</stackView> </stackView>
</subviews> </subviews>
<constraints> <constraints>
@@ -462,7 +535,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="149"/> <rect key="frame" x="0.0" y="0.0" width="375" height="149"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="center" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="Jvb-r8-XrY"> <stackView opaque="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" axis="vertical" distribution="equalSpacing" alignment="center" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="Jvb-r8-XrY">
<rect key="frame" x="0.0" y="0.0" width="375" height="149"/> <rect key="frame" x="0.0" y="0.0" width="375" height="149"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Permissions" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dj7-G8-GFv"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="Permissions" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dj7-G8-GFv">
@@ -559,8 +632,11 @@ World</string>
<outlet property="descriptionTextView" destination="Pyt-8D-BZA" id="cgV-Hg-LrH"/> <outlet property="descriptionTextView" destination="Pyt-8D-BZA" id="cgV-Hg-LrH"/>
<outlet property="permissionsCollectionView" destination="r8T-dj-wQX" id="Xud-5X-w2E"/> <outlet property="permissionsCollectionView" destination="r8T-dj-wQX" id="Xud-5X-w2E"/>
<outlet property="screenshotsCollectionView" destination="ppk-lL-at8" id="YoQ-Z6-WTP"/> <outlet property="screenshotsCollectionView" destination="ppk-lL-at8" id="YoQ-Z6-WTP"/>
<outlet property="sizeLabel" destination="DgM-bD-bBY" id="Oky-ax-u20"/>
<outlet property="subtitleLabel" destination="BsL-O2-UjD" id="cfe-cf-4a9"/> <outlet property="subtitleLabel" destination="BsL-O2-UjD" id="cfe-cf-4a9"/>
<outlet property="versionDateLabel" destination="wGD-mS-8fO" id="icB-lC-g9x"/>
<outlet property="versionDescriptionTextView" destination="wQF-WY-Gdz" id="rdV-rY-VAz"/> <outlet property="versionDescriptionTextView" destination="wQF-WY-Gdz" id="rdV-rY-VAz"/>
<outlet property="versionLabel" destination="7E0-TV-G4l" id="Vb4-SR-E2o"/>
</connections> </connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dhh-ZN-LoG" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="dhh-ZN-LoG" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -950,7 +1026,7 @@ World</string>
</namedColor> </namedColor>
</resources> </resources>
<inferredMetricsTieBreakers> <inferredMetricsTieBreakers>
<segue reference="cnd-KK-o60"/> <segue reference="gjN-bg-1G5"/>
</inferredMetricsTieBreakers> </inferredMetricsTieBreakers>
<color key="tintColor" name="Green"/> <color key="tintColor" name="Green"/>
</document> </document>

View File

@@ -31,4 +31,16 @@ class NavigationBar: UINavigationBar
self.barTintColor = .white self.barTintColor = .white
self.shadowImage = UIImage() self.shadowImage = UIImage()
} }
override func layoutSubviews()
{
super.layoutSubviews()
// We can't easily shift just the back button up, so we shift the entire content view slightly.
for contentView in self.subviews
{
guard NSStringFromClass(type(of: contentView)).contains("ContentView") else { continue }
contentView.center.y -= 2
}
}
} }

View File

@@ -0,0 +1,34 @@
//
// Date+RelativeDate.swift
// AltStore
//
// Created by Riley Testut on 7/28/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
extension Date
{
func numberOfCalendarDays(since date: Date) -> Int
{
let today = Calendar.current.startOfDay(for: self)
let previousDay = Calendar.current.startOfDay(for: date)
let components = Calendar.current.dateComponents([.day], from: previousDay, to: today)
return components.day!
}
func relativeDateString(since date: Date, dateFormatter: DateFormatter) -> String
{
let numberOfDays = self.numberOfCalendarDays(since: date)
switch numberOfDays
{
case 0: return NSLocalizedString("Today", comment: "")
case 1: return NSLocalizedString("Yesterday", comment: "")
case 2...7: return String(format: NSLocalizedString("%@ days ago", comment: ""), NSNumber(value: numberOfDays))
default: return dateFormatter.string(from: date)
}
}
}

View File

@@ -25,18 +25,6 @@ extension MyAppsViewController
} }
} }
private extension Date
{
func numberOfCalendarDays(since date: Date) -> Int
{
let today = Calendar.current.startOfDay(for: self)
let previousDay = Calendar.current.startOfDay(for: date)
let components = Calendar.current.dateComponents([.day], from: previousDay, to: today)
return components.day!
}
}
class MyAppsViewController: UICollectionViewController class MyAppsViewController: UICollectionViewController
{ {
private lazy var dataSource = self.makeDataSource() private lazy var dataSource = self.makeDataSource()
@@ -190,16 +178,7 @@ private extension MyAppsViewController
let progress = AppManager.shared.installationProgress(for: app) let progress = AppManager.shared.installationProgress(for: app)
cell.updateButton.progress = progress cell.updateButton.progress = progress
cell.dateLabel.text = self.dateFormatter.string(from: app.versionDate) cell.dateLabel.text = Date().relativeDateString(since: app.versionDate, dateFormatter: self.dateFormatter)
let numberOfDays = Date().numberOfCalendarDays(since: app.versionDate)
switch numberOfDays
{
case 0: cell.dateLabel.text = NSLocalizedString("Today", comment: "")
case 1: cell.dateLabel.text = NSLocalizedString("Yesterday", comment: "")
case 2...7: cell.dateLabel.text = String(format: NSLocalizedString("%@ days ago", comment: ""), NSNumber(value: numberOfDays))
default: cell.dateLabel.text = self.dateFormatter.string(from: app.versionDate)
}
cell.setNeedsLayout() cell.setNeedsLayout()
} }