mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-17 10:43:30 +01:00
[ADD] iOS 13 compatible AsyncImage implementation with cache
This commit is contained in:
@@ -17,6 +17,9 @@
|
|||||||
191E607E290B2EA7001A3B7C /* jplist.c in Sources */ = {isa = PBXBuildFile; fileRef = 191E5FCF290A651D001A3B7C /* jplist.c */; };
|
191E607E290B2EA7001A3B7C /* jplist.c in Sources */ = {isa = PBXBuildFile; fileRef = 191E5FCF290A651D001A3B7C /* jplist.c */; };
|
||||||
1920B04F2924AC8300744F60 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 1920B04E2924AC8300744F60 /* Settings.bundle */; };
|
1920B04F2924AC8300744F60 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 1920B04E2924AC8300744F60 /* Settings.bundle */; };
|
||||||
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */; };
|
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */; };
|
||||||
|
1F0DD810293222DF007608A4 /* AsyncImage in Frameworks */ = {isa = PBXBuildFile; productRef = 1F0DD80F293222DF007608A4 /* AsyncImage */; };
|
||||||
|
1F0DD81329322487007608A4 /* LazyScrollingVStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD81229322487007608A4 /* LazyScrollingVStack.swift */; };
|
||||||
|
1F0DD81C2932D2FF007608A4 /* AppScreenshotsScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */; };
|
||||||
1F6E08DA292806E0005059C0 /* AppRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08D9292806E0005059C0 /* AppRowView.swift */; };
|
1F6E08DA292806E0005059C0 /* AppRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08D9292806E0005059C0 /* AppRowView.swift */; };
|
||||||
1F6E08DC292807D3005059C0 /* AppIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08DB292807D3005059C0 /* AppIconView.swift */; };
|
1F6E08DC292807D3005059C0 /* AppIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08DB292807D3005059C0 /* AppIconView.swift */; };
|
||||||
1F6E08E029280B12005059C0 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08DF29280B12005059C0 /* SafariView.swift */; };
|
1F6E08E029280B12005059C0 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F6E08DF29280B12005059C0 /* SafariView.swift */; };
|
||||||
@@ -537,6 +540,8 @@
|
|||||||
191E5FD1290A651D001A3B7C /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jsmn.h; path = Dependencies/libplist/src/jsmn.h; sourceTree = SOURCE_ROOT; };
|
191E5FD1290A651D001A3B7C /* jsmn.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = jsmn.h; path = Dependencies/libplist/src/jsmn.h; sourceTree = SOURCE_ROOT; };
|
||||||
1920B04E2924AC8300744F60 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
1920B04E2924AC8300744F60 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||||
19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; };
|
19B9B7442845E6DF0076EF69 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; };
|
||||||
|
1F0DD81229322487007608A4 /* LazyScrollingVStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyScrollingVStack.swift; sourceTree = "<group>"; };
|
||||||
|
1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppScreenshotsScrollView.swift; sourceTree = "<group>"; };
|
||||||
1F6E08D9292806E0005059C0 /* AppRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRowView.swift; sourceTree = "<group>"; };
|
1F6E08D9292806E0005059C0 /* AppRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRowView.swift; sourceTree = "<group>"; };
|
||||||
1F6E08DB292807D3005059C0 /* AppIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconView.swift; sourceTree = "<group>"; };
|
1F6E08DB292807D3005059C0 /* AppIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconView.swift; sourceTree = "<group>"; };
|
||||||
1F6E08DF29280B12005059C0 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
1F6E08DF29280B12005059C0 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||||
@@ -979,6 +984,7 @@
|
|||||||
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
||||||
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */,
|
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */,
|
||||||
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */,
|
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */,
|
||||||
|
1F0DD810293222DF007608A4 /* AsyncImage in Frameworks */,
|
||||||
BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */,
|
BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */,
|
||||||
B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */,
|
B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */,
|
||||||
BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */,
|
BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */,
|
||||||
@@ -1024,6 +1030,14 @@
|
|||||||
path = "libimobiledevice-glue/src";
|
path = "libimobiledevice-glue/src";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
1F0DD81129322472007608A4 /* Layout */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
1F0DD81229322487007608A4 /* LazyScrollingVStack.swift */,
|
||||||
|
);
|
||||||
|
path = Layout;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
1F6E08DD29280AF1005059C0 /* View Extensions */ = {
|
1F6E08DD29280AF1005059C0 /* View Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1089,6 +1103,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
1FB84BA52928DE08006A5CF4 /* AppDetailView.swift */,
|
1FB84BA52928DE08006A5CF4 /* AppDetailView.swift */,
|
||||||
|
1F0DD81B2932D2FF007608A4 /* AppScreenshotsScrollView.swift */,
|
||||||
);
|
);
|
||||||
path = "App Detail";
|
path = "App Detail";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1129,6 +1144,7 @@
|
|||||||
1FB84BA72928E073006A5CF4 /* View Components */ = {
|
1FB84BA72928E073006A5CF4 /* View Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
1F0DD81129322472007608A4 /* Layout */,
|
||||||
1F6E08D9292806E0005059C0 /* AppRowView.swift */,
|
1F6E08D9292806E0005059C0 /* AppRowView.swift */,
|
||||||
1F6E08DB292807D3005059C0 /* AppIconView.swift */,
|
1F6E08DB292807D3005059C0 /* AppIconView.swift */,
|
||||||
1F6E08E529280F4B005059C0 /* RatingStars.swift */,
|
1F6E08E529280F4B005059C0 /* RatingStars.swift */,
|
||||||
@@ -2195,6 +2211,7 @@
|
|||||||
4879A9612861049C00FC1BBD /* OpenSSL */,
|
4879A9612861049C00FC1BBD /* OpenSSL */,
|
||||||
9922FFEB29B501C50020F868 /* Starscream */,
|
9922FFEB29B501C50020F868 /* Starscream */,
|
||||||
1FB96FB729297C11007E68D1 /* GridStack */,
|
1FB96FB729297C11007E68D1 /* GridStack */,
|
||||||
|
1F0DD80F293222DF007608A4 /* AsyncImage */,
|
||||||
);
|
);
|
||||||
productName = AltStore;
|
productName = AltStore;
|
||||||
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
||||||
@@ -2268,6 +2285,7 @@
|
|||||||
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
||||||
9922FFEA29B501C50020F868 /* XCRemoteSwiftPackageReference "Starscream" */,
|
9922FFEA29B501C50020F868 /* XCRemoteSwiftPackageReference "Starscream" */,
|
||||||
1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */,
|
1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */,
|
||||||
|
1F0DD80E293222DF007608A4 /* XCRemoteSwiftPackageReference "AsyncImage" */,
|
||||||
);
|
);
|
||||||
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -2686,6 +2704,7 @@
|
|||||||
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */,
|
||||||
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */,
|
||||||
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
BFE60740231AFD2A002B0E8E /* InsetGroupTableViewCell.swift in Sources */,
|
||||||
|
1F0DD81329322487007608A4 /* LazyScrollingVStack.swift in Sources */,
|
||||||
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
BF0DCA662433BDF500E3A595 /* AnalyticsManager.swift in Sources */,
|
||||||
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
BFCCB51A245E3401001853EA /* VerifyAppOperation.swift in Sources */,
|
||||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */,
|
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */,
|
||||||
@@ -2752,6 +2771,7 @@
|
|||||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||||
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */,
|
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */,
|
||||||
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */,
|
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */,
|
||||||
|
1F0DD81C2932D2FF007608A4 /* AppScreenshotsScrollView.swift in Sources */,
|
||||||
BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */,
|
BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */,
|
||||||
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */,
|
1FB96FCF292BBBCA007E68D1 /* SiriShortcutSetupView.swift in Sources */,
|
||||||
BFF0B6942321CB85007A79E1 /* AuthenticationViewController.swift in Sources */,
|
BFF0B6942321CB85007A79E1 /* AuthenticationViewController.swift in Sources */,
|
||||||
@@ -3582,6 +3602,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
1F0DD80E293222DF007608A4 /* XCRemoteSwiftPackageReference "AsyncImage" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/zzzzeu/AsyncImage";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 0.0.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */ = {
|
1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/pietropizzi/GridStack";
|
repositoryURL = "https://github.com/pietropizzi/GridStack";
|
||||||
@@ -3683,6 +3711,11 @@
|
|||||||
package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */;
|
package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */;
|
||||||
productName = OpenSSL;
|
productName = OpenSSL;
|
||||||
};
|
};
|
||||||
|
1F0DD80F293222DF007608A4 /* AsyncImage */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 1F0DD80E293222DF007608A4 /* XCRemoteSwiftPackageReference "AsyncImage" */;
|
||||||
|
productName = AsyncImage;
|
||||||
|
};
|
||||||
1FB96FB729297C11007E68D1 /* GridStack */ = {
|
1FB96FB729297C11007E68D1 /* GridStack */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */;
|
package = 1FB96FB629297C11007E68D1 /* XCRemoteSwiftPackageReference "GridStack" */;
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
"version" : "4.4.2"
|
"version" : "4.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"identity" : "asyncimage",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/zzzzeu/AsyncImage",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "854d01f6bb9550f4aeee8959ab5b67d7d7775f02",
|
||||||
|
"version" : "0.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"identity" : "gridstack",
|
"identity" : "gridstack",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AsyncImage
|
||||||
|
|
||||||
struct AppIconView: View {
|
struct AppIconView: View {
|
||||||
let iconUrl: URL?
|
let iconUrl: URL?
|
||||||
@@ -16,7 +17,7 @@ struct AppIconView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if let iconUrl, #available(iOS 15.0, *) {
|
if let iconUrl {
|
||||||
AsyncImage(url: iconUrl) { image in
|
AsyncImage(url: iconUrl) { image in
|
||||||
image
|
image
|
||||||
.resizable()
|
.resizable()
|
||||||
@@ -25,10 +26,6 @@ struct AppIconView: View {
|
|||||||
}
|
}
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
|
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
|
||||||
} else {
|
|
||||||
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
|
||||||
.background(Color.secondary)
|
|
||||||
.frame(width: size, height: size)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import GridStack
|
import GridStack
|
||||||
|
import AsyncImage
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
|
|
||||||
struct AppDetailView: View {
|
struct AppDetailView: View {
|
||||||
@@ -39,7 +40,7 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ObservableScrollView(scrollOffset: self.$scrollOffset) { proxy in
|
ObservableScrollView(scrollOffset: $scrollOffset) { proxy in
|
||||||
LazyVStack {
|
LazyVStack {
|
||||||
headerView
|
headerView
|
||||||
.frame(height: headerViewHeight)
|
.frame(height: headerViewHeight)
|
||||||
@@ -86,29 +87,35 @@ struct AppDetailView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var contentView: some View {
|
var contentView: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading, spacing: 24) {
|
||||||
if let subtitle = storeApp.subtitle {
|
if let subtitle = storeApp.subtitle {
|
||||||
Text(subtitle)
|
Text(subtitle)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
.padding()
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !storeApp.screenshotURLs.isEmpty {
|
if !storeApp.screenshotURLs.isEmpty {
|
||||||
screenshotsView
|
// Equatable: Only reload the view if the screenshots change.
|
||||||
|
// This prevents unnecessary redraws on scroll.
|
||||||
|
AppScreenshotsScrollView(urls: storeApp.screenshotURLs)
|
||||||
|
.equatable()
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(storeApp.localizedDescription)
|
Text(storeApp.localizedDescription)
|
||||||
.lineLimit(6)
|
.lineLimit(6)
|
||||||
.padding()
|
.padding(.horizontal)
|
||||||
|
|
||||||
currentVersionView
|
currentVersionView
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
permissionsView
|
permissionsView
|
||||||
|
.padding(.horizontal)
|
||||||
|
|
||||||
// TODO: Add review view
|
// TODO: Add review view
|
||||||
// Only let users rate the app if it is currently installed!
|
// Only let users rate the app if it is currently installed!
|
||||||
}
|
}
|
||||||
|
.padding(.vertical)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: contentCornerRadius)
|
RoundedRectangle(cornerRadius: contentCornerRadius)
|
||||||
.foregroundColor(Color(UIColor.systemBackground))
|
.foregroundColor(Color(UIColor.systemBackground))
|
||||||
@@ -116,28 +123,6 @@ struct AppDetailView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
var screenshotsView: some View {
|
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
|
||||||
HStack {
|
|
||||||
ForEach(storeApp.screenshotURLs) { url in
|
|
||||||
if #available(iOS 15.0, *) {
|
|
||||||
AsyncImage(url: url) { image in
|
|
||||||
image.resizable()
|
|
||||||
} placeholder: {
|
|
||||||
Rectangle()
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
.aspectRatio(9/16, contentMode: .fit)
|
|
||||||
.cornerRadius(8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
|
||||||
.frame(height: 400)
|
|
||||||
.shadow(radius: 12)
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentVersionView: some View {
|
var currentVersionView: some View {
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
HStack(alignment: .bottom) {
|
HStack(alignment: .bottom) {
|
||||||
@@ -169,7 +154,6 @@ struct AppDetailView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var permissionsView: some View {
|
var permissionsView: some View {
|
||||||
@@ -199,12 +183,5 @@ struct AppDetailView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//struct AppDetailView_Previews: PreviewProvider {
|
|
||||||
// static var previews: some View {
|
|
||||||
// AppDetailView()
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|||||||
49
AltStore/Views/App Detail/AppScreenshotsScrollView.swift
Normal file
49
AltStore/Views/App Detail/AppScreenshotsScrollView.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// AppScreenshotsScrollView.swift
|
||||||
|
// SideStore
|
||||||
|
//
|
||||||
|
// Created by Fabian Thies on 27.11.22.
|
||||||
|
// Copyright © 2022 SideStore. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import AsyncImage
|
||||||
|
|
||||||
|
|
||||||
|
/// Horizontal ScrollView with an asynchronously loaded image for each screenshot URL
|
||||||
|
///
|
||||||
|
/// The struct inherits the `Equatable` protocol and implements the respective comparisation function to prevent the view from being constantly re-rendered when a `@State` change in the parent view occurs.
|
||||||
|
/// This way, the `AppScreenshotsScrollView` will only be reloaded when the parameters change.
|
||||||
|
struct AppScreenshotsScrollView: View {
|
||||||
|
let urls: [URL]
|
||||||
|
var aspectRatio: CGFloat = 9/16
|
||||||
|
var height: CGFloat = 400
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
HStack {
|
||||||
|
ForEach(urls) { url in
|
||||||
|
AsyncImage(url: url) { image in
|
||||||
|
image
|
||||||
|
.resizable()
|
||||||
|
} placeholder: {
|
||||||
|
Rectangle()
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
.aspectRatio(aspectRatio, contentMode: .fit)
|
||||||
|
.cornerRadius(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
}
|
||||||
|
.frame(height: height)
|
||||||
|
.shadow(radius: 12)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppScreenshotsScrollView: Equatable {
|
||||||
|
/// Prevent re-rendering of the view if the parameters didn't change
|
||||||
|
static func == (lhs: AppScreenshotsScrollView, rhs: AppScreenshotsScrollView) -> Bool {
|
||||||
|
lhs.urls == rhs.urls && lhs.aspectRatio == rhs.aspectRatio && lhs.height == rhs.height
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AsyncImage
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
|
|
||||||
struct BrowseAppPreviewView: View {
|
struct BrowseAppPreviewView: View {
|
||||||
@@ -23,16 +24,15 @@ struct BrowseAppPreviewView: View {
|
|||||||
if !storeApp.screenshotURLs.isEmpty {
|
if !storeApp.screenshotURLs.isEmpty {
|
||||||
HStack {
|
HStack {
|
||||||
ForEach(storeApp.screenshotURLs.prefix(2)) { url in
|
ForEach(storeApp.screenshotURLs.prefix(2)) { url in
|
||||||
if #available(iOS 15.0, *) {
|
AsyncImage(url: url) { image in
|
||||||
AsyncImage(url: url) { image in
|
image
|
||||||
image
|
.resizable()
|
||||||
.resizable()
|
.aspectRatio(contentMode: .fit)
|
||||||
.aspectRatio(contentMode: .fit)
|
} placeholder: {
|
||||||
} placeholder: {
|
Color(UIColor.secondarySystemBackground)
|
||||||
Color(UIColor.secondarySystemBackground)
|
.aspectRatio(9/16, contentMode: .fit)
|
||||||
}
|
|
||||||
.cornerRadius(8)
|
|
||||||
}
|
}
|
||||||
|
.cornerRadius(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(height: 300)
|
.frame(height: 300)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AsyncImage
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
|
|
||||||
struct NewsItemView: View {
|
struct NewsItemView: View {
|
||||||
@@ -53,17 +54,15 @@ struct NewsItemView: View {
|
|||||||
}
|
}
|
||||||
.padding(24)
|
.padding(24)
|
||||||
|
|
||||||
if let imageUrl = newsItem.imageURL, #available(iOS 15.0, *) {
|
if let imageUrl = newsItem.imageURL {
|
||||||
AsyncImage(
|
AsyncImage(url: imageUrl) { image in
|
||||||
url: imageUrl,
|
image
|
||||||
content: { image in
|
.resizable()
|
||||||
if let image = image.image {
|
.aspectRatio(contentMode: .fit)
|
||||||
image
|
} placeholder: {
|
||||||
.resizable()
|
Color.secondary
|
||||||
.aspectRatio(contentMode: .fit)
|
.frame(maxWidth: .infinity, maxHeight: 100)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(
|
.frame(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import AsyncImage
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
import Intents
|
import Intents
|
||||||
|
|
||||||
@@ -86,6 +87,9 @@ struct SettingsView: View {
|
|||||||
Text("Switch to UIKit")
|
Text("Switch to UIKit")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwiftUI.Button(action: resetImageCache) {
|
||||||
|
Text("Reset Image Cache")
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Debug")
|
Text("Debug")
|
||||||
}
|
}
|
||||||
@@ -137,6 +141,19 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
UIApplication.shared.keyWindow?.rootViewController = rootVC
|
UIApplication.shared.keyWindow?.rootViewController = rootVC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetImageCache() {
|
||||||
|
do {
|
||||||
|
let url = try FileManager.default.url(
|
||||||
|
for: .cachesDirectory,
|
||||||
|
in: .userDomainMask,
|
||||||
|
appropriateFor: nil,
|
||||||
|
create: true)
|
||||||
|
try FileManager.default.removeItem(at: url.appendingPathComponent("com.zeu.cache", isDirectory: true))
|
||||||
|
} catch let error {
|
||||||
|
fatalError("\(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsView_Previews: PreviewProvider {
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
@@ -144,3 +161,5 @@ struct SettingsView_Previews: PreviewProvider {
|
|||||||
SettingsView()
|
SettingsView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user