diff --git a/AltStore/App Detail/AppDetailView.swift b/AltStore/App Detail/AppDetailView.swift index 5159fab4..2697177f 100644 --- a/AltStore/App Detail/AppDetailView.swift +++ b/AltStore/App Detail/AppDetailView.swift @@ -145,7 +145,7 @@ struct AppDetailView: View { private let inset: CGFloat = 15 private let padding: CGFloat = 20 private let bannerHeight: CGFloat = AppBannerView.standardHeight - private let backButtonSize: CGFloat = 40 + private let backButtonSize: CGFloat = 44 var body: some View { GeometryReader { geo in @@ -443,9 +443,9 @@ struct AppDetailView: View { return HStack { SwiftUI.Button(action: { model.pop() }) { Image(systemName: "chevron.backward") - .font(.body.weight(.semibold)) + .font(.title3.weight(.semibold)) .foregroundStyle(Color(uiColor: model.app.tintColor ?? .altPrimary)) - .frame(width: 40, height: 40) + .frame(width: 44, height: 44) .background(.ultraThinMaterial, in: Circle()) } .accessibilityLabel(NSLocalizedString("Back", comment: "")) @@ -597,6 +597,9 @@ private struct AppScreenshotsRow: View { } .scrollTargetBehavior(.viewAligned) .frame(height: 400) + // Clip the carousel to its own band so thumbnails never bleed past the rounded + // content card during a scroll/overscroll (they "scrolled off" the card edges). + .clipped() .padding(.bottom, 20) } } @@ -606,12 +609,13 @@ private struct DetailScreenshotView: View { private let preferredHeight: CGFloat = 400 - private var aspectRatio: CGFloat { - var r = screenshot.aspectRatio - if r.width > r.height && screenshot.deviceType == .iphone { - r = CGSize(width: r.height, height: r.width) - } - return r.width / r.height + // Uniform display aspect: every thumbnail is framed to the same modern-iPhone + // portrait ratio and aspect-FILLED (scaledToFill + clipped below), so screenshots + // whose real aspect differs from the frame — including any letterbox padding baked + // into the source image — crop to fill instead of showing black bars. A single shared + // width also lets the viewAligned carousel snap cleanly with no per-image width jitter. + private var frameWidth: CGFloat { + preferredHeight * (AppScreenshot.defaultAspectRatio.width / AppScreenshot.defaultAspectRatio.height) } @State private var image: UIImage? @@ -623,13 +627,13 @@ private struct DetailScreenshotView: View { Image(uiImage: img) .resizable() .scaledToFill() - .frame(width: preferredHeight * aspectRatio, height: preferredHeight) + .frame(width: frameWidth, height: preferredHeight) .clipped() } else { ProgressView() } } - .frame(width: preferredHeight * aspectRatio, height: preferredHeight) + .frame(width: frameWidth, height: preferredHeight) .clipShape(RoundedRectangle(cornerRadius: 5, style: .continuous)) .overlay( RoundedRectangle(cornerRadius: 5, style: .continuous) diff --git a/AltStore/Authentication/InstructionsView.swift b/AltStore/Authentication/InstructionsView.swift index f661d22f..d26d1258 100644 --- a/AltStore/Authentication/InstructionsView.swift +++ b/AltStore/Authentication/InstructionsView.swift @@ -23,17 +23,17 @@ struct InstructionsView: View { private let steps: [Step] = [ Step(id: 1, - title: NSLocalizedString("Launch SideStore", comment: ""), - detail: NSLocalizedString("Leave SideStore running in the background on your idevice.", comment: "")), + title: NSLocalizedString("Launch MiniStore", comment: ""), + detail: NSLocalizedString("Leave MiniStore running in the background on your idevice.", comment: "")), Step(id: 2, title: NSLocalizedString("Connect to Wi-Fi and VPN", comment: ""), - detail: NSLocalizedString("Enable LocalDevVPN and use Sidestore on the go.", comment: "")), + detail: NSLocalizedString("Enable LocalDevVPN and use MiniStore on the go.", comment: "")), Step(id: 3, title: NSLocalizedString("Download Apps", comment: ""), - detail: NSLocalizedString("Browse and download apps directly from SideStore.", comment: "")), + detail: NSLocalizedString("Browse and download apps directly from MiniStore.", comment: "")), Step(id: 4, title: NSLocalizedString("Apps Refresh Automatically", comment: ""), - detail: NSLocalizedString("Apps are refreshed in the background while you are on SideStore VPN!", comment: "")), + detail: NSLocalizedString("Apps are refreshed in the background while you are on MiniStore VPN!", comment: "")), ] // Device-fixed: true only on screens under 600 pt tall (e.g. iPhone SE 1st gen). diff --git a/AltStore/My Apps/MyAppsView.swift b/AltStore/My Apps/MyAppsView.swift index 80a9a9bd..fbb15a46 100644 --- a/AltStore/My Apps/MyAppsView.swift +++ b/AltStore/My Apps/MyAppsView.swift @@ -460,17 +460,30 @@ private struct MyAppsSectionHeader: View { SwiftUI.Button { onButton() } label: { - if isSpinning { - ProgressView() - .tint(Color(.altPrimary)) - .accessibilityLabel(NSLocalizedString("Refreshing", comment: "")) - } else { - // foregroundStyle, not .tint: .tint does not color plain-style - // button text, so the accent never reached these labels. + ZStack { + // Reserve the idle title's footprint (kept in the layout, just + // hidden) so the header's trailing edge doesn't shift when the + // spinner swaps in — the button stays symmetric across the + // idle/refreshing states. Text(bt) - .font(.subheadline) - .foregroundStyle(Color(.altPrimary)) + .font(.subheadline.weight(.semibold)) + .opacity(isSpinning ? 0 : 1) + .accessibilityHidden(isSpinning) + if isSpinning { + ProgressView() + .controlSize(.small) + .tint(Color(.altPrimary)) + .accessibilityLabel(NSLocalizedString("Refreshing", comment: "")) + } } + // foregroundStyle, not .tint: .tint does not color plain-style + // button text, so the accent never reached these labels. + .foregroundStyle(Color(.altPrimary)) + // Extend the hit area toward the title and vertically without moving + // the glyph, so the small label is easier to press. + .padding(.vertical, 8) + .padding(.leading, 16) + .contentShape(Rectangle()) } .buttonStyle(.plain) } else if let icon = buttonIcon { @@ -478,8 +491,11 @@ private struct MyAppsSectionHeader: View { onButton() } label: { Image(systemName: icon) - .font(.subheadline) + .font(.subheadline.weight(.semibold)) .foregroundStyle(Color(.altPrimary)) + .padding(.vertical, 8) + .padding(.leading, 16) + .contentShape(Rectangle()) } .buttonStyle(.plain) } diff --git a/AltStore/Sources/SourceDetailView.swift b/AltStore/Sources/SourceDetailView.swift index 7961e190..976fa4f3 100644 --- a/AltStore/Sources/SourceDetailView.swift +++ b/AltStore/Sources/SourceDetailView.swift @@ -319,7 +319,7 @@ struct SourceDetailView: View { GeometryReader { geo in let width = geo.size.width let safeTop = geo.safeAreaInsets.top - let backButtonSize: CGFloat = 40 + let backButtonSize: CGFloat = 44 let minContentHeight = safeTop + backButtonSize + 8 + headerHeight + DetailLayout.padding let maxContentY = max(width * 0.667, minContentHeight) @@ -566,9 +566,9 @@ struct SourceDetailView: View { model.hostViewController?.navigationController?.popViewController(animated: true) } label: { Image(systemName: "chevron.backward") - .font(.body.weight(.semibold)) + .font(.title3.weight(.semibold)) .foregroundStyle(Color(uiColor: source.effectiveTintColor?.adjustedForDisplay ?? .altPrimary)) - .frame(width: 40, height: 40) + .frame(width: 44, height: 44) .background(.ultraThinMaterial, in: Circle()) } .accessibilityLabel(NSLocalizedString("Back", comment: ""))