Skip to content

Commit

Permalink
Fix unclickable elements
Browse files Browse the repository at this point in the history
The introduction of iOS 18 brought a new bug that made `KFAnimatedImage`
not recognize tap gestures and become unclickable. (onevcat/Kingfisher#2295)

This commit addresses the issue with a workaround found here:
onevcat/Kingfisher#2046 (comment)

The workaround was suggested by the author of the library to fix a
slightly different issue, but that property seems to work for our
purposes.

The issue is addressed by adding a `contentShape` property to usages
of `KFAnimatedImage`, in order to make them clickable. A custom modifier
was created to make the solution less obscure and more obvious.

Furthermore, one empty tap gesture handler was removed as it was
preventing other tap gesture handlers on the image carousel from being
triggered on iOS 18

Testing
-------

PASS

Configurations:
- iPhone 13 mini on iOS 18.0
- iPhone SE simulator on iOS 17.5
Damus: This commit
Coverage:
- Check that the following views are clickable:
    - Images in the carousel
    - Profile picture on notes
    - Profile picture on thread comments
    - Profile picture on profile page

Changelog-Fixed: Fix items that became unclickable on iOS 18
Closes: damus-io#2342
Closes: damus-io#2370
Signed-off-by: Daniel D’Aquino <[email protected]>
  • Loading branch information
danieldaquino committed Sep 21, 2024
1 parent 6254cea commit 823c256
Show file tree
Hide file tree
Showing 13 changed files with 57 additions and 2 deletions.
14 changes: 14 additions & 0 deletions damus.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,8 @@
D7CE1B482B0BE719002EDAD4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C32B93D2A9AD44700DC3548 /* Message.swift */; };
D7CE1B492B0BE729002EDAD4 /* DisplayName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C9BB83029C0ED4F00FC4E37 /* DisplayName.swift */; };
D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; };
D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; };
D7D68FFA2C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; };
D7DBD41F2B02F15E002A6197 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; };
D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; };
D7EDED152B11776B0018B19C /* LibreTranslateServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE45AF5297BB2E700C1D842 /* LibreTranslateServer.swift */; };
Expand Down Expand Up @@ -1959,6 +1961,7 @@
D7CBD1D32B8D21DC00BFD889 /* DamusPurpleNotificationManagement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleNotificationManagement.swift; sourceTree = "<group>"; };
D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleImpendingExpirationTests.swift; sourceTree = "<group>"; };
D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationClient.swift; sourceTree = "<group>"; };
D7D68FF82C9E01B60015A515 /* KFClickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFClickable.swift; sourceTree = "<group>"; };
D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrEventTests.swift; sourceTree = "<group>"; };
D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; };
D7EDED1D2B11797D0018B19C /* LongformEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongformEvent.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2540,6 +2543,7 @@
4C75EFA227FA576C0006080F /* Views */ = {
isa = PBXGroup;
children = (
D7D68FF72C9E01A80015A515 /* Utils */,
D78DB85D2C20FE9E00F0AB12 /* Chat */,
D71AC4CA2BA8E3320076268E /* Extensions */,
BA3759952ABCCF360018D73B /* Camera */,
Expand Down Expand Up @@ -3368,6 +3372,14 @@
path = Extensions;
sourceTree = "<group>";
};
D7D68FF72C9E01A80015A515 /* Utils */ = {
isa = PBXGroup;
children = (
D7D68FF82C9E01B60015A515 /* KFClickable.swift */,
);
path = Utils;
sourceTree = "<group>";
};
E06336A72B7582D600A88E6B /* Assets */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3803,6 +3815,7 @@
B51C1CEA2B55A60A00E312A9 /* AddMuteItemView.swift in Sources */,
4C5D5C992A6AF8F80024563C /* NdbNote.swift in Sources */,
4CF0ABF029857E9200D66079 /* Bech32Object.swift in Sources */,
D7D68FFA2C9E01BE0015A515 /* KFClickable.swift in Sources */,
4C3D52B8298DB5C6001C5831 /* TextEvent.swift in Sources */,
4C216F362870A9A700040376 /* InputDismissKeyboard.swift in Sources */,
D74AAFCF2B155D8C006CF0F4 /* ZapDataModel.swift in Sources */,
Expand Down Expand Up @@ -4517,6 +4530,7 @@
D73E5F332C6A97F4007EB227 /* ZapEvent.swift in Sources */,
D73E5F342C6A97F4007EB227 /* TextEvent.swift in Sources */,
D73E5F352C6A97F4007EB227 /* WideEventView.swift in Sources */,
D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */,
D73E5F8A2C6AA69C007EB227 /* SideMenuView.swift in Sources */,
D73E5F362C6A97F4007EB227 /* LongformView.swift in Sources */,
D73E5F372C6A97F4007EB227 /* LongformPreview.swift in Sources */,
Expand Down
11 changes: 9 additions & 2 deletions damus/Components/ImageCarousel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ struct ImageCarousel<Content: View>: View {
Placeholder(url: url, geo_size: geo.size, num_urls: urls.count)
}
.aspectRatio(contentMode: filling ? .fill : .fit)
.kfClickable()
.position(x: geo.size.width / 2, y: geo.size.height / 2)
.tabItem {
Text(url.absoluteString)
Expand Down Expand Up @@ -274,8 +275,14 @@ struct ImageCarousel<Content: View>: View {

var body: some View {
VStack {
Medias
.onTapGesture { }
if #available(iOS 18.0, *) {
Medias
} else {
// An empty tap gesture recognizer is needed on iOS 17 and below to suppress other overlapping tap recognizers
// Otherwise it will both open the carousel and go to a note at the same time
Medias.onTapGesture { }
}


if urls.count > 1 {
PageControlView(currentPage: $model.selectedIndex, numberOfPages: urls.count)
Expand Down
2 changes: 2 additions & 0 deletions damus/Views/BannerImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct EditBannerImageView: View {
Color(uiColor: .secondarySystemBackground)
}
.onFailureImage(defaultImage)
.kfClickable()

EditPictureControl(uploader: damus_state.settings.default_media_uploader, pubkey: damus_state.pubkey, image_url: $banner_image, uploadObserver: viewModel, callback: callback)
}
Expand All @@ -54,6 +55,7 @@ struct InnerBannerImageView: View {
Color(uiColor: .secondarySystemBackground)
}
.onFailureImage(defaultImage)
.kfClickable()
} else {
Image(uiImage: defaultImage).resizable()
}
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Events/Highlight/HighlightEventRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct HighlightEventRef: View {
FailedImage()
}
.frame(width: 35, height: 35)
.kfClickable()
.clipShape(RoundedRectangle(cornerRadius: 10))
.overlay(RoundedRectangle(cornerRadius: 10).stroke(.gray.opacity(0.5), lineWidth: 0.5))
.scaledToFit()
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Events/Highlight/HighlightLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct HighlightLink: View {
.background(DamusColors.adaptableWhite)
}
.frame(width: 35, height: 35)
.kfClickable()
.clipShape(RoundedRectangle(cornerRadius: 10))
.scaledToFit()
} else {
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Events/Longform/LongformPreview.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ struct LongformPreviewBody: View {
}
.aspectRatio(contentMode: .fill)
.frame(maxWidth: .infinity, maxHeight: header ? .infinity : 150)
.kfClickable()
.cornerRadius(1)
}

Expand Down
1 change: 1 addition & 0 deletions damus/Views/Images/ImageContainerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct ImageContainerView: View {
view.framePreloadCount = 3
}
.imageModifier(ImageHandler(handler: $image))
.kfClickable()
.clipped()
.modifier(ImageContextMenuModifier(url: url, image: image, settings: settings, showShareSheet: $showShareSheet))
.sheet(isPresented: $showShareSheet) {
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Images/ProfilePicImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct ProfileImageContainerView: View {
.imageModifier(ImageHandler(handler: $image))
.clipShape(Circle())
.modifier(ImageContextMenuModifier(url: url, image: image, settings: settings, showShareSheet: $showShareSheet))
.kfClickable()
.sheet(isPresented: $showShareSheet) {
ShareSheet(activityItems: [url])
}
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Profile/EditPictureControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ struct EditPictureControl: View {
}
.scaledToFill()
.frame(width: (size ?? 25) + 10, height: (size ?? 25) + 10)
.kfClickable()
.foregroundColor(DamusColors.white)
.clipShape(Circle())
.overlay(Circle().stroke(.white, lineWidth: 4))
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Profile/ProfilePicView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ struct InnerProfilePicView: View {
}
.scaledToFill()
.frame(width: size, height: size)
.kfClickable()
.clipShape(Circle())
.overlay(Circle().stroke(highlight_color(highlight), lineWidth: pfp_line_width(highlight)))
}
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Profile/ProfilePictureSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ struct EditProfilePictureView: View {
view.framePreloadCount = 3
}
.scaledToFill()
.kfClickable()

EditPictureControl(uploader: damus_state?.settings.default_media_uploader ?? .nostrBuild, pubkey: pubkey, image_url: $profile_url, uploadObserver: uploadObserver, callback: callback)
}
Expand Down
1 change: 1 addition & 0 deletions damus/Views/Relays/RelayPicView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct InnerRelayPicView: View {
Placeholder(url: url)
}
.scaledToFit()
.kfClickable()
} else {
FailedRelayImage(url: nil)
}
Expand Down
23 changes: 23 additions & 0 deletions damus/Views/Utils/KFClickable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// ClickableOverlay.swift
// damus
//
// Created by Daniel D’Aquino on 2024-09-20.
//

import SwiftUI

/// Applies a property that makes `KFAnimatedImage` clickable again on iOS 18+
fileprivate struct KFClickable: ViewModifier {
func body(content: Content) -> some View {
content
.contentShape(Rectangle())
}
}

extension View {
/// Applies a property that makes `KFAnimatedImage` clickable again on iOS 18+
func kfClickable() -> some View {
return self.modifier(KFClickable())
}
}

0 comments on commit 823c256

Please sign in to comment.