Fix isues on iPad

This commit is contained in:
Marcin Czachurski 2023-09-16 10:39:21 +02:00
parent ed94179b1d
commit 3171022870
18 changed files with 179 additions and 55 deletions

View File

@ -18,6 +18,7 @@ public class ImageSizeService {
/// Cache with orginal image sizes.
private var memoryCacheData = MemoryCache<URL, CGSize>(entryLifetime: 3600)
private let staticImageHeight = 500.0
public func get(for url: URL) -> CGSize? {
return self.memoryCacheData[url]
@ -37,6 +38,12 @@ public class ImageSizeService {
}
extension ImageSizeService {
public func calculate(for url: URL) -> CGSize {
return UIDevice.current.userInterfaceIdiom == .phone
? ImageSizeService.shared.calculate(for: url, andContainerWidth: UIScreen.main.bounds.size.width)
: ImageSizeService.shared.calculate(for: url, andContainerHeight: self.staticImageHeight)
}
public func calculate(for url: URL, andContainerWidth containerWidth: Double) -> CGSize {
guard let size = self.get(for: url) else {
return CGSize(width: containerWidth, height: containerWidth)
@ -45,6 +52,20 @@ extension ImageSizeService {
return self.calculate(width: size.width, height: size.height, andContainerWidth: containerWidth)
}
public func calculate(for url: URL, andContainerHeight containerHeight: Double) -> CGSize {
guard let size = self.get(for: url) else {
return CGSize(width: containerHeight, height: containerHeight)
}
return self.calculate(width: size.width, height: size.height, andContainerHeight: containerHeight)
}
public func calculate(width: Double, height: Double) -> CGSize {
return UIDevice.current.userInterfaceIdiom == .phone
? ImageSizeService.shared.calculate(width: width, height: height, andContainerWidth: UIScreen.main.bounds.size.width)
: ImageSizeService.shared.calculate(width: width, height: height, andContainerHeight: self.staticImageHeight)
}
public func calculate(width: Double, height: Double, andContainerWidth containerWidth: Double) -> CGSize {
let divider = Double(width) / containerWidth
let calculatedHeight = Double(height) / divider
@ -56,4 +77,16 @@ extension ImageSizeService {
return size
}
public func calculate(width: Double, height: Double, andContainerHeight containerHeight: Double) -> CGSize {
let divider = Double(height) / containerHeight
let calculatedWidth = Double(width) / divider
let size = CGSize(
width: (calculatedWidth > 0 && calculatedWidth < .infinity) ? calculatedWidth : containerHeight,
height: containerHeight
)
return size
}
}

View File

@ -200,6 +200,7 @@
F8F6E44D29BCC1F90004795E /* PhotoMediumWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F6E44829BCC0F00004795E /* PhotoMediumWidgetView.swift */; };
F8F6E44E29BCC1FB0004795E /* PhotoLargeWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F6E44A29BCC0FF0004795E /* PhotoLargeWidgetView.swift */; };
F8F6E45129BCE9190004795E /* UIImage+Resize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F6E45029BCE9190004795E /* UIImage+Resize.swift */; };
F8FAA0AD2AB0BCB400FD78BD /* View+ContainerBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8FAA0AC2AB0BCB400FD78BD /* View+ContainerBackground.swift */; };
F8FB8ABA29EB2ED400342C04 /* NavigationMenuButtons.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8FB8AB929EB2ED400342C04 /* NavigationMenuButtons.swift */; };
/* End PBXBuildFile section */
@ -407,6 +408,7 @@
F8F6E44829BCC0F00004795E /* PhotoMediumWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoMediumWidgetView.swift; sourceTree = "<group>"; };
F8F6E44A29BCC0FF0004795E /* PhotoLargeWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLargeWidgetView.swift; sourceTree = "<group>"; };
F8F6E45029BCE9190004795E /* UIImage+Resize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Resize.swift"; sourceTree = "<group>"; };
F8FAA0AC2AB0BCB400FD78BD /* View+ContainerBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+ContainerBackground.swift"; sourceTree = "<group>"; };
F8FB8AB929EB2ED400342C04 /* NavigationMenuButtons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationMenuButtons.swift; sourceTree = "<group>"; };
F8FFBD4929E99BEE0047EE80 /* Vernissage-009.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Vernissage-009.xcdatamodel"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -909,6 +911,7 @@
children = (
F815F60B29E49CF20044566B /* Avatar.swift */,
F8F6E45029BCE9190004795E /* UIImage+Resize.swift */,
F8FAA0AC2AB0BCB400FD78BD /* View+ContainerBackground.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -1121,6 +1124,7 @@
F864F78229BB9A6500B13921 /* StatusData+CoreDataClass.swift in Sources */,
F864F78329BB9A6800B13921 /* StatusData+CoreDataProperties.swift in Sources */,
F864F78429BB9A6E00B13921 /* ApplicationSettings+CoreDataClass.swift in Sources */,
F8FAA0AD2AB0BCB400FD78BD /* View+ContainerBackground.swift in Sources */,
F864F78629BB9A7400B13921 /* AccountData+CoreDataClass.swift in Sources */,
F8705A7B29FF872F00DA818A /* QRCodeGenerator.swift in Sources */,
F865B4CE2A024AD8008ACDFC /* StatusData+Faulty.swift in Sources */,
@ -1552,7 +1556,7 @@
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 143;
CURRENT_PROJECT_VERSION = 200;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1571,7 +1575,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.9.0;
MARKETING_VERSION = 1.10.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -1594,7 +1598,7 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
CODE_SIGN_ENTITLEMENTS = Vernissage/Vernissage.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 143;
CURRENT_PROJECT_VERSION = 200;
DEVELOPMENT_ASSET_PATHS = "\"Vernissage/Preview Content\"";
DEVELOPMENT_TEAM = B2U9FEKYP8;
ENABLE_PREVIEWS = YES;
@ -1613,7 +1617,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.9.0;
MARKETING_VERSION = 1.10.0;
PRODUCT_BUNDLE_IDENTIFIER = dev.mczachurski.vernissage;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

View File

@ -56,7 +56,7 @@ struct StatusView: View {
private func mainBody() -> some View {
switch state {
case .loading:
StatusPlaceholderView(imageHeight: self.getImageHeight(), imageBlurhash: self.imageBlurhash)
StatusPlaceholderView(imageWidth: self.getImageWidth(), imageHeight: self.getImageHeight(), imageBlurhash: self.imageBlurhash)
.task {
await self.loadData()
}
@ -236,13 +236,12 @@ struct StatusView: View {
private func getImageHeight() -> Double {
if let highestImageUrl = self.highestImageUrl, let imageSize = ImageSizeService.shared.get(for: highestImageUrl) {
return imageSize.height
let calculatedSize = ImageSizeService.shared.calculate(width: imageSize.width, height: imageSize.height)
return calculatedSize.height
}
if let imageHeight = self.imageHeight, let imageWidth = self.imageWidth, imageHeight > 0 && imageWidth > 0 {
let calculatedSize = ImageSizeService.shared.calculate(width: Double(imageWidth),
height: Double(imageHeight),
andContainerWidth: UIScreen.main.bounds.size.width)
let calculatedSize = ImageSizeService.shared.calculate(width: Double(imageWidth), height: Double(imageHeight))
return calculatedSize.height
}
@ -250,6 +249,21 @@ struct StatusView: View {
return UIScreen.main.bounds.width * 0.75
}
private func getImageWidth() -> Double {
if let highestImageUrl = self.highestImageUrl, let imageSize = ImageSizeService.shared.get(for: highestImageUrl) {
let calculatedSize = ImageSizeService.shared.calculate(width: imageSize.width, height: imageSize.height)
return calculatedSize.width
}
if let imageHeight = self.imageHeight, let imageWidth = self.imageWidth, imageHeight > 0 && imageWidth > 0 {
let calculatedSize = ImageSizeService.shared.calculate(width: Double(imageWidth), height: Double(imageHeight))
return calculatedSize.width
}
// If we don't have image height and width in metadata, we have to use some constant height.
return UIScreen.main.bounds.width * 0.75
}
private func getMainStatus(status: StatusModel) async throws -> StatusModel {
guard let inReplyToId = status.inReplyToId else {
return status

View File

@ -8,6 +8,7 @@ import SwiftUI
import WidgetsKit
struct StatusPlaceholderView: View {
@State var imageWidth: Double
@State var imageHeight: Double
@State var imageBlurhash: String?
@ -17,11 +18,11 @@ struct StatusPlaceholderView: View {
if let imageBlurhash, let uiImage = UIImage(blurHash: imageBlurhash, size: CGSize(width: 32, height: 32)) {
Image(uiImage: uiImage)
.resizable()
.frame(width: UIScreen.main.bounds.width, height: imageHeight)
.frame(width: self.imageWidth, height: self.imageHeight)
} else {
Rectangle()
.fill(Color.placeholderText)
.frame(width: UIScreen.main.bounds.width, height: imageHeight)
.frame(width: self.imageWidth, height: self.imageHeight)
.redacted(reason: .placeholder)
}

View File

@ -101,7 +101,6 @@ struct StatusesView: View {
ImageRowAsync(statusViewModel: item, containerWidth: $containerWidth)
} onLoadMore: {
do {
print("load more......")
try await self.loadMoreStatuses()
} catch {
ErrorService.shared.handle(error, message: "statuses.error.loadingStatusesFailed", showToastr: !Task.isCancelled)

View File

@ -57,6 +57,8 @@ struct UserProfileHeaderView: View {
.opacity(0.6)
}
}.foregroundColor(.mainTextColor)
Spacer()
}
HStack(alignment: .center) {

View File

@ -12,11 +12,29 @@ import WidgetsKit
struct ImageCarouselPicture: View {
@ObservedObject public var attachment: AttachmentModel
@State private var blurredImageHeight: Double
@State private var blurredImageWidth: Double
private let onImageDownloaded: (AttachmentModel, Data) -> Void
init(attachment: AttachmentModel, onImageDownloaded: @escaping (_: AttachmentModel, _: Data) -> Void) {
self.attachment = attachment
self.onImageDownloaded = onImageDownloaded
if let size = ImageSizeService.shared.get(for: attachment.url) {
let imageSize = ImageSizeService.shared.calculate(width: size.width, height: size.height)
self.blurredImageHeight = imageSize.height
self.blurredImageWidth = imageSize.width
} else if let imageWidth = attachment.metaImageWidth, let imageHeight = attachment.metaImageHeight {
let imageSize = ImageSizeService.shared.calculate(width: Double(imageWidth), height: Double(imageHeight))
self.blurredImageHeight = imageSize.height
self.blurredImageWidth = imageSize.width
} else {
self.blurredImageHeight = 100.0
self.blurredImageWidth = 100.0
}
}
var body: some View {
@ -26,6 +44,7 @@ struct ImageCarouselPicture: View {
.aspectRatio(contentMode: .fit)
} else {
BlurredImage(blurhash: attachment.blurhash)
.frame(width: self.blurredImageWidth, height: self.blurredImageHeight)
.task {
do {
// Download image and recalculate exif data.

View File

@ -47,9 +47,7 @@ struct ImagesCarousel: View {
// Calculate size of frame (first from cache, then from metadata).
if let highestImage, let size = ImageSizeService.shared.get(for: highestImage.url) {
let calculatedSize = ImageSizeService.shared.calculate(width: size.width,
height: size.height,
andContainerWidth: UIScreen.main.bounds.size.width)
let calculatedSize = ImageSizeService.shared.calculate(width: size.width, height: size.height)
self.imageWidth = calculatedSize.width
self.imageHeight = calculatedSize.height
@ -57,7 +55,8 @@ struct ImagesCarousel: View {
self.heightWasPrecalculated = true
} else if let highestImage, imgHeight > 0 && imgWidth > 0 {
ImageSizeService.shared.save(for: highestImage.url, width: imgWidth, height: imgHeight)
let size = ImageSizeService.shared.calculate(for: highestImage.url, andContainerWidth: UIScreen.main.bounds.size.width)
let size = ImageSizeService.shared.calculate(for: highestImage.url)
self.imageWidth = size.width
self.imageHeight = size.height
@ -70,33 +69,37 @@ struct ImagesCarousel: View {
}
var body: some View {
TabView(selection: $selected) {
ForEach(attachments, id: \.id) { attachment in
ImageCarouselPicture(attachment: attachment) { (attachment, imageData) in
withAnimation {
self.recalculateImageHeight(attachment: attachment, imageData: imageData)
}
HStack {
Spacer()
TabView(selection: $selected) {
ForEach(attachments, id: \.id) { attachment in
ImageCarouselPicture(attachment: attachment) { (attachment, imageData) in
withAnimation {
self.recalculateImageHeight(attachment: attachment, imageData: imageData)
}
self.asyncAfter(0.4) {
attachment.set(data: imageData)
}
self.asyncAfter(0.4) {
attachment.set(data: imageData)
}
}
.tag(attachment.id)
}
.tag(attachment.id)
}
.frame(height: self.imageHeight)
.tabViewStyle(PageTabViewStyle())
.onChange(of: selected, perform: { index in
if let attachment = attachments.first(where: { item in item.id == index }) {
self.selectedAttachment = attachment
self.exifCamera = attachment.exifCamera
self.exifExposure = attachment.exifExposure
self.exifCreatedDate = attachment.exifCreatedDate
self.exifLens = attachment.exifLens
self.description = attachment.description
}
})
Spacer()
}
.frame(height: self.imageHeight)
.tabViewStyle(PageTabViewStyle())
.onChange(of: selected, perform: { index in
if let attachment = attachments.first(where: { item in item.id == index }) {
self.selectedAttachment = attachment
self.exifCamera = attachment.exifCamera
self.exifExposure = attachment.exifExposure
self.exifCreatedDate = attachment.exifCreatedDate
self.exifLens = attachment.exifLens
self.description = attachment.description
}
})
.onAppear {
self.selected = self.attachments.first?.id ?? String.empty()
}
@ -107,28 +110,41 @@ struct ImagesCarousel: View {
return
}
var imageHeight = 0.0
var imageWidth = 0.0
var maxImageHeight = 0.0
var maxImageWidth = 0.0
for item in attachments {
if let data = item.data, let image = UIImage(data: data) {
ImageSizeService.shared.save(for: attachment.url, width: image.size.width, height: image.size.height)
// Get attachment sizes from cache.
if let attachmentSize = ImageSizeService.shared.get(for: item.url) {
if attachmentSize.height > maxImageHeight {
maxImageHeight = attachmentSize.height
maxImageWidth = attachmentSize.width
}
if image.size.height > imageHeight {
imageHeight = image.size.height
imageWidth = image.size.width
continue
}
// When we don't have in cache read from data and add to cache.
if let data = item.data, let image = UIImage(data: data) {
ImageSizeService.shared.save(for: item.url, width: image.size.width, height: image.size.height)
if image.size.height > maxImageHeight {
maxImageHeight = image.size.height
maxImageWidth = image.size.width
}
}
}
if let image = UIImage(data: imageData) {
if image.size.height > imageHeight {
imageHeight = image.size.height
imageWidth = image.size.width
ImageSizeService.shared.save(for: attachment.url, width: image.size.width, height: image.size.height)
if image.size.height > maxImageHeight {
maxImageHeight = image.size.height
maxImageWidth = image.size.width
}
}
let size = ImageSizeService.shared.calculate(for: attachment.url, andContainerWidth: UIScreen.main.bounds.size.width)
let size = ImageSizeService.shared.calculate(width: maxImageWidth, height: maxImageHeight)
self.imageWidth = size.width
self.imageHeight = size.height
}

View File

@ -0,0 +1,29 @@
//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import SwiftUI
extension View {
// func widgetBackground(backgroundView: some View) -> some View {
// if #available(iOSApplicationExtension 17.0, *) {
// return containerBackground(for: .widget) {
// backgroundView
// }
// } else {
// return background(backgroundView)
// }
// }
func widgetBackground<V>(@ViewBuilder content: @escaping () -> V) -> some View where V: View {
if #available(iOSApplicationExtension 17.0, *) {
return containerBackground(for: .widget) {
content()
}
} else {
return background(content())
}
}
}

View File

@ -17,5 +17,6 @@ struct PhotoWidget: Widget {
.configurationDisplayName("Vernissage")
.description("widget.title.photoDescription")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
.contentMarginsDisabled()
}
}

View File

@ -38,7 +38,7 @@ struct PhotoLargeWidgetView: View {
.padding(.leading, 8)
.padding(.bottom, 8)
}
.background {
.widgetBackground {
uiImage
.resizable()
.aspectRatio(contentMode: .fill)

View File

@ -38,7 +38,7 @@ struct PhotoMediumWidgetView: View {
.padding(.leading, 8)
.padding(.bottom, 8)
}
.background {
.widgetBackground {
uiImage
.resizable()
.aspectRatio(contentMode: .fill)

View File

@ -33,7 +33,7 @@ struct PhotoSmallWidgetView: View {
.padding(.leading, 8)
.padding(.bottom, 8)
}
.background {
.widgetBackground {
uiImage
.resizable()
.aspectRatio(contentMode: .fill)

View File

@ -17,5 +17,6 @@ struct QRCodeWidget: Widget {
.configurationDisplayName("Vernissage")
.description("widget.title.qrCodeDescription")
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
.contentMarginsDisabled()
}
}

View File

@ -82,6 +82,8 @@ struct QRCodeLargeWidgetView: View {
.offset(y: -8)
}
}
.widgetBackground {
}
.padding([.leading, .trailing, .top], 24)
}
}

View File

@ -92,6 +92,8 @@ struct QRCodeMediumWidgetView: View {
.padding(.leading, 3)
.offset(y: -4)
}
.widgetBackground {
}
.padding([.leading, .trailing, .top], 12)
}
}

View File

@ -61,6 +61,8 @@ struct QRCodeSmallWidgetView: View {
.frame(height: 24)
}
}
.widgetBackground {
}
.padding(8)
}
}

View File

@ -52,10 +52,10 @@ struct DeviceImageGallery: ViewModifier {
}
}
.onAppear {
asyncAfter(0.1) {
// asyncAfter(0.1) {
let galleryProperties = self.getGalleryProperties(geometry: geometry, horizontalSize: self.horizontalSizeClass ?? .compact)
self.action(galleryProperties)
}
// }
}
}
}
@ -69,7 +69,6 @@ struct DeviceImageGallery: ViewModifier {
} else {
// View like on iPad.
let imageColumns = geometry.size.width > geometry.size.height ? 3 : 2
print("\(geometry.size.width ):\(geometry.size.height)")
return GalleryProperties(imageColumns: imageColumns,
containerWidth: geometry.size.width / Double(imageColumns),