mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-12 16:23:32 +01:00
Supports both iPhone + iPad screenshots
Prefers showing screenshots for current device, but falls back to all screenshots if there are no relevant ones.
This commit is contained in:
@@ -186,7 +186,7 @@ extension AppContentViewController
|
||||
switch Row.allCases[indexPath.row]
|
||||
{
|
||||
case .screenshots:
|
||||
guard !self.app.screenshots.isEmpty else { return 0.0 }
|
||||
guard !self.app.allScreenshots.isEmpty else { return 0.0 }
|
||||
return UITableView.automaticDimension
|
||||
|
||||
case .permissions:
|
||||
|
||||
@@ -93,6 +93,25 @@ class AppScreenshotCollectionViewCell: UICollectionViewCell
|
||||
}
|
||||
}
|
||||
|
||||
extension AppScreenshotCollectionViewCell
|
||||
{
|
||||
func setImage(_ image: UIImage?)
|
||||
{
|
||||
guard var image, let cgImage = image.cgImage else {
|
||||
self.imageView.image = image
|
||||
return
|
||||
}
|
||||
|
||||
if image.size.width > image.size.height && self.aspectRatio.width < self.aspectRatio.height
|
||||
{
|
||||
// Image is landscape, but cell has portrait aspect ratio, so rotate image to match.
|
||||
image = UIImage(cgImage: cgImage, scale: image.scale, orientation: .right)
|
||||
}
|
||||
|
||||
self.imageView.image = image
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppScreenshotCollectionViewCell
|
||||
{
|
||||
func updateAspectRatio()
|
||||
|
||||
@@ -98,16 +98,29 @@ private extension AppScreenshotsViewController
|
||||
|
||||
func makeDataSource() -> RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>
|
||||
{
|
||||
let dataSource = RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>(items: self.app.screenshots)
|
||||
dataSource.cellConfigurationHandler = { (cell, screenshot, indexPath) in
|
||||
let screenshots = self.app.preferredScreenshots()
|
||||
|
||||
let dataSource = RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>(items: screenshots)
|
||||
dataSource.cellConfigurationHandler = { [weak self] (cell, screenshot, indexPath) in
|
||||
let cell = cell as! AppScreenshotCollectionViewCell
|
||||
cell.imageView.image = nil
|
||||
cell.imageView.isIndicatingActivity = true
|
||||
cell.setImage(nil)
|
||||
|
||||
var aspectRatio = screenshot.size ?? AppScreenshot.defaultAspectRatio
|
||||
if aspectRatio.width > aspectRatio.height
|
||||
{
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
switch screenshot.deviceType
|
||||
{
|
||||
case .iphone:
|
||||
// Always rotate landscape iPhone screenshots regardless of horizontal size class.
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
|
||||
case .ipad where self?.traitCollection.horizontalSizeClass == .compact:
|
||||
// Only rotate landscape iPad screenshots if we're in horizontally compact environment.
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
cell.aspectRatio = aspectRatio
|
||||
@@ -115,7 +128,7 @@ private extension AppScreenshotsViewController
|
||||
dataSource.prefetchHandler = { (screenshot, indexPath, completionHandler) in
|
||||
let imageURL = screenshot.imageURL
|
||||
return RSTAsyncBlockOperation() { (operation) in
|
||||
let request = ImageRequest(url: imageURL, processors: [.screenshot])
|
||||
let request = ImageRequest(url: imageURL)
|
||||
ImagePipeline.shared.loadImage(with: request, progress: nil) { result in
|
||||
guard !operation.isCancelled else { return operation.finish() }
|
||||
|
||||
@@ -130,7 +143,7 @@ private extension AppScreenshotsViewController
|
||||
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||
let cell = cell as! AppScreenshotCollectionViewCell
|
||||
cell.imageView.isIndicatingActivity = false
|
||||
cell.imageView.image = image
|
||||
cell.setImage(image)
|
||||
|
||||
if let error = error
|
||||
{
|
||||
|
||||
@@ -106,16 +106,29 @@ private extension PreviewAppScreenshotsViewController
|
||||
|
||||
func makeDataSource() -> RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>
|
||||
{
|
||||
let dataSource = RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>(items: self.app.screenshots)
|
||||
dataSource.cellConfigurationHandler = { (cell, screenshot, indexPath) in
|
||||
let screenshots = self.app.preferredScreenshots()
|
||||
|
||||
let dataSource = RSTArrayCollectionViewPrefetchingDataSource<AppScreenshot, UIImage>(items: screenshots)
|
||||
dataSource.cellConfigurationHandler = { [weak self] (cell, screenshot, indexPath) in
|
||||
let cell = cell as! AppScreenshotCollectionViewCell
|
||||
cell.imageView.image = nil
|
||||
cell.imageView.isIndicatingActivity = true
|
||||
cell.setImage(nil)
|
||||
|
||||
var aspectRatio = screenshot.size ?? AppScreenshot.defaultAspectRatio
|
||||
if aspectRatio.width > aspectRatio.height
|
||||
{
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
switch screenshot.deviceType
|
||||
{
|
||||
case .iphone:
|
||||
// Always rotate landscape iPhone screenshots regardless of horizontal size class.
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
|
||||
case .ipad where self?.traitCollection.horizontalSizeClass == .compact:
|
||||
// Only rotate landscape iPad screenshots if we're in horizontally compact environment.
|
||||
aspectRatio = CGSize(width: aspectRatio.height, height: aspectRatio.width)
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
cell.aspectRatio = aspectRatio
|
||||
@@ -123,7 +136,7 @@ private extension PreviewAppScreenshotsViewController
|
||||
dataSource.prefetchHandler = { (screenshot, indexPath, completionHandler) in
|
||||
let imageURL = screenshot.imageURL
|
||||
return RSTAsyncBlockOperation() { (operation) in
|
||||
let request = ImageRequest(url: imageURL, processors: [.screenshot])
|
||||
let request = ImageRequest(url: imageURL)
|
||||
ImagePipeline.shared.loadImage(with: request, progress: nil) { result in
|
||||
guard !operation.isCancelled else { return operation.finish() }
|
||||
|
||||
@@ -138,7 +151,7 @@ private extension PreviewAppScreenshotsViewController
|
||||
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||
let cell = cell as! AppScreenshotCollectionViewCell
|
||||
cell.imageView.isIndicatingActivity = false
|
||||
cell.imageView.image = image
|
||||
cell.setImage(image)
|
||||
|
||||
if let error = error
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@ private extension BrowseCollectionViewCell
|
||||
}
|
||||
dataSource.prefetchHandler = { (imageURL, indexPath, completionHandler) in
|
||||
return RSTAsyncBlockOperation() { (operation) in
|
||||
let request = ImageRequest(url: imageURL as URL, processors: [.screenshot])
|
||||
let request = ImageRequest(url: imageURL as URL)
|
||||
ImagePipeline.shared.loadImage(with: request, progress: nil) { result in
|
||||
guard !operation.isCancelled else { return operation.finish() }
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ extension SourceError
|
||||
case duplicate
|
||||
|
||||
case missingPermissionUsageDescription
|
||||
case missingScreenshotSize
|
||||
}
|
||||
|
||||
static func unsupported(_ source: Source) -> SourceError { SourceError(code: .unsupported, source: source) }
|
||||
@@ -36,6 +37,10 @@ extension SourceError
|
||||
static func missingPermissionUsageDescription(for permission: any ALTAppPermission, app: StoreApp, source: Source) -> SourceError {
|
||||
SourceError(code: .missingPermissionUsageDescription, source: source, app: app, permission: permission)
|
||||
}
|
||||
|
||||
static func missingScreenshotSize(for screenshot: AppScreenshot, source: Source) -> SourceError {
|
||||
SourceError(code: .missingScreenshotSize, source: source, app: screenshot.app, screenshotURL: screenshot.imageURL)
|
||||
}
|
||||
}
|
||||
|
||||
struct SourceError: ALTLocalizedError
|
||||
@@ -59,6 +64,9 @@ struct SourceError: ALTLocalizedError
|
||||
@UserInfoValue
|
||||
var permission: (any ALTAppPermission)?
|
||||
|
||||
@UserInfoValue
|
||||
var screenshotURL: URL?
|
||||
|
||||
var errorFailureReason: String {
|
||||
switch self.code
|
||||
{
|
||||
@@ -112,6 +120,14 @@ struct SourceError: ALTLocalizedError
|
||||
let permissionType = permission.type.localizedName ?? NSLocalizedString("Permission", comment: "")
|
||||
let failureReason = String(format: NSLocalizedString("The %@ '%@' for %@ is missing a usage description.", comment: ""), permissionType.lowercased(), permission.rawValue, appName)
|
||||
return failureReason
|
||||
|
||||
case .missingScreenshotSize:
|
||||
let appName = self.$app.name ?? String(format: NSLocalizedString("an app in source “%@”", comment: ""), self.$source.name)
|
||||
let baseMessage = String(format: NSLocalizedString("An iPad screenshot for %@ does not specify its size", comment: ""), appName)
|
||||
guard let screenshotURL else { return baseMessage + "." }
|
||||
|
||||
let failureReason = baseMessage + ": \(screenshotURL.absoluteString)"
|
||||
return failureReason
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -209,6 +209,12 @@ private extension FetchSourceOperation
|
||||
// Privacy permissions MUST have a usage description.
|
||||
guard permission.usageDescription != nil else { throw SourceError.missingPermissionUsageDescription(for: permission.permission, app: app, source: source) }
|
||||
}
|
||||
|
||||
for screenshot in app.screenshots(for: .ipad)
|
||||
{
|
||||
// All iPad screenshots MUST have an explicit size.
|
||||
guard screenshot.size != nil else { throw SourceError.missingScreenshotSize(for: screenshot, source: source) }
|
||||
}
|
||||
}
|
||||
|
||||
if let previousSourceID = self.$source.identifier
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// ScreenshotProcessor.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 4/11/22.
|
||||
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Nuke
|
||||
|
||||
struct ScreenshotProcessor: ImageProcessing
|
||||
{
|
||||
var identifier: String { "io.altstore.ScreenshotProcessor" }
|
||||
|
||||
func process(_ image: PlatformImage) -> PlatformImage?
|
||||
{
|
||||
guard let cgImage = image.cgImage, image.size.width > image.size.height else { return image }
|
||||
|
||||
let rotatedImage = UIImage(cgImage: cgImage, scale: image.scale, orientation: .right)
|
||||
return rotatedImage
|
||||
}
|
||||
}
|
||||
|
||||
extension ImageProcessing where Self == ScreenshotProcessor
|
||||
{
|
||||
static var screenshot: Self { Self() }
|
||||
}
|
||||
Reference in New Issue
Block a user