From e27a249c8273c8db8abe3415a435816046b50b9d Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Sun, 15 Nov 2026 11:42:43 +0100 Subject: [PATCH 1/2] feat: enhance photo picker navigation and date scrubbing Add date scrubber configuration and iOS 26 glass styling, scroll edge effects, and accessible navigation buttons. Update Swift dependencies and bump the build number to 3. Signed-off-by: Marino Faggiana --- Nextcloud.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/swiftpm/Package.resolved | 34 ++++----- iOSClient/Main/NCPickerViewController.swift | 69 +++++++++++++++++-- 3 files changed, 84 insertions(+), 23 deletions(-) diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index df72a92be1..77b9b47e1f 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -6667,7 +6667,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = NKUJUXUJ3B; @@ -6735,7 +6735,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = NKUJUXUJ3B; diff --git a/Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 4e862cb430..10fc3fdd33 100644 --- a/Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Nextcloud.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/app-check.git", "state" : { - "revision" : "c7eb17e5545f36e43dc50e70da09c2e999714e16", - "version" : "12.0.0" + "revision" : "b7c71458b70284cc0f5a87103f66f0b1d98b1873", + "version" : "12.1.0" } }, { @@ -96,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleUtilities.git", "state" : { - "revision" : "cc31f5ed464b111f877ce8554cbef4455d410d91", - "version" : "8.1.4" + "revision" : "5459ee5603def3db0fe14ca522185f42c48d978e", + "version" : "8.1.5" } }, { @@ -105,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/groue/GRDB.swift", "state" : { - "revision" : "b83108d10f42680d78f23fe4d4d80fc88dab3212", - "version" : "7.11.1" + "revision" : "92a554cd0d5e8d67cb18993c6054e73fe42c066c", + "version" : "7.12.0" } }, { @@ -205,7 +205,7 @@ "location" : "https://github.com/nextcloud/NextcloudKit", "state" : { "branch" : "main", - "revision" : "62d52ad3ac203eebb80c4999ae304d4fc6da6f3b" + "revision" : "b1bbf831daca1cca49a7b5637e3cf290b7a9393e" } }, { @@ -222,8 +222,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/exyte/PopupView.git", "state" : { - "revision" : "99695337ce9e965b70e4aa14b49b1a9920017333", - "version" : "5.0.8" + "revision" : "38fe3de1d9b455a77dd119794e2fa22bdea3ba7f", + "version" : "5.0.9" } }, { @@ -276,8 +276,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "35a1edcb8ebd2bfe251d587f0b69e62af28f508c", - "version" : "1.7.3" + "revision" : "88754449ab766a739610b660e117762115fabc9c", + "version" : "1.7.6" } }, { @@ -285,8 +285,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "6db9d9abd92ad7399b8a564cafbb87d3507245fa", - "version" : "2.104.0" + "revision" : "2f58d8ef84438d6517fc78bcea2f8527e3c72936", + "version" : "2.105.0" } }, { @@ -303,8 +303,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system.git", "state" : { - "revision" : "869129b7bf4ecc57b97d0193ad29690ca2134750", - "version" : "1.8.1" + "revision" : "640e5755b5826f4437eaeedd366fd776737db62c", + "version" : "1.9.0" } }, { @@ -357,8 +357,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tilltue/TLPhotoPicker", "state" : { - "revision" : "91d7bca65721928af30c6a2bb1a1f517f7862964", - "version" : "2.1.21" + "revision" : "4e948c4eecae0a164b746767701ed805a6e904d0", + "version" : "2.2.0" } }, { diff --git a/iOSClient/Main/NCPickerViewController.swift b/iOSClient/Main/NCPickerViewController.swift index bc2320b02c..3783dad8e4 100644 --- a/iOSClient/Main/NCPickerViewController.swift +++ b/iOSClient/Main/NCPickerViewController.swift @@ -57,6 +57,14 @@ class NCPhotosPickerViewController: NSObject { configure.selectedColor = NCBrandColor.shared.getElement(account: controller.account) configure.singleSelectedMode = singleSelectedMode configure.allowedAlbumCloudShared = true + var dateScrubber = TLDateScrubberConfigure() + dateScrubber.minimumContentScreens = 2 + if #available(iOS 26.0, *) { + dateScrubber.thumbColor = UIColor.secondarySystemBackground.withAlphaComponent(0.78) + dateScrubber.labelBackgroundColor = UIColor.secondarySystemBackground.withAlphaComponent(0.88) + dateScrubber.labelTextColor = .label + } + configure.dateScrubber = dateScrubber pickerVC = customPhotoPickerViewController(withTLPHAssets: { assets in pickerVC?.dismiss(animated: true) { @@ -112,6 +120,8 @@ class NCPhotosPickerViewController: NSObject { class customPhotoPickerViewController: TLPhotosPickerViewController { var ncController: NCMainTabBarController? + private let navigationSymbolConfiguration = UIImage.SymbolConfiguration(pointSize: 14, weight: .medium) + private let navigationVerticalPadding: CGFloat = 10 override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent @@ -124,12 +134,50 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { self.customNavItem.leftBarButtonItem?.tintColor = NCBrandColor.shared.iconImageColor self.customNavItem.rightBarButtonItem?.tintColor = NCBrandColor.shared.iconImageColor if #available(iOS 26.0, *) { - doneButton.image = UIImage(systemName: "checkmark") - cancelButton.image = UIImage(systemName: "xmark") - navigationBarTopConstraint.constant = self.navigationBarTopConstraint.constant + 10 + doneButton.image = UIImage(systemName: "checkmark", withConfiguration: navigationSymbolConfiguration) + doneButton.accessibilityLabel = configure.doneTitle + configureAlbumTitleBackground() + configureNavigationScrollEdge() } } + @available(iOS 26.0, *) + private func configureAlbumTitleBackground() { + let background = UIVisualEffectView(effect: UIGlassEffect(style: .regular)) + background.cornerConfiguration = .capsule() + background.isUserInteractionEnabled = false + background.translatesAutoresizingMaskIntoConstraints = false + titleView.insertSubview(background, at: 0) + + NSLayoutConstraint.activate([ + background.leadingAnchor.constraint(equalTo: titleView.leadingAnchor, constant: -10), + background.trailingAnchor.constraint(equalTo: titleView.trailingAnchor, constant: 10), + background.topAnchor.constraint(equalTo: titleView.topAnchor), + background.bottomAnchor.constraint(equalTo: titleView.bottomAnchor) + ]) + } + + @available(iOS 26.0, *) + private func configureNavigationScrollEdge() { + guard let collectionTopConstraint = view.constraints.first(where: { + $0.firstItem === collectionView && $0.firstAttribute == .top && + $0.secondItem === navigationBar && $0.secondAttribute == .bottom + }) else { return } + + // The picker XIB starts the grid below the navigation bar. + collectionTopConstraint.isActive = false + collectionView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true + navigationBarTopConstraint.constant = navigationVerticalPadding + navigationBar.isTranslucent = true + view.bringSubviewToFront(navigationBar) + + collectionView.topEdgeEffect.style = .soft + let interaction = UIScrollEdgeElementContainerInteraction() + interaction.scrollView = collectionView + interaction.edge = .top + navigationBar.addInteraction(interaction) + } + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) applyCustomButtons() @@ -137,6 +185,16 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() + if #available(iOS 26.0, *), navigationBar.isTranslucent { + let topInset = navigationBar.frame.maxY - view.safeAreaInsets.top + navigationVerticalPadding + if collectionView.contentInset.top != topInset { + let isAtTop = collectionView.contentOffset.y <= -collectionView.adjustedContentInset.top + 1 + collectionView.contentInset.top = topInset + if isAtTop && !collectionView.isDragging { + collectionView.contentOffset.y = -collectionView.adjustedContentInset.top + } + } + } applyCustomButtons() } @@ -154,6 +212,10 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { action: #selector(customAction) ) closeBtn.tintColor = NCBrandColor.shared.iconImageColor + closeBtn.accessibilityLabel = configure.cancelTitle + if #available(iOS 26.0, *) { + closeBtn.image = UIImage(systemName: "xmark", withConfiguration: navigationSymbolConfiguration) + } var leftItems: [UIBarButtonItem] = [closeBtn] @@ -195,7 +257,6 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { // MARK: - Document Picker class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate { - let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! let utilityFileSystem = NCUtilityFileSystem() let database = NCManageDatabase.shared From 7a0e8d9b6d8ec37d27b2a75a9309852c14c607a5 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Sun, 15 Nov 2026 11:56:30 +0100 Subject: [PATCH 2/2] fix: apply iOS 26 glass styling to photo picker album popup Signed-off-by: Marino Faggiana --- iOSClient/Main/NCPickerViewController.swift | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/iOSClient/Main/NCPickerViewController.swift b/iOSClient/Main/NCPickerViewController.swift index 3783dad8e4..254dbed810 100644 --- a/iOSClient/Main/NCPickerViewController.swift +++ b/iOSClient/Main/NCPickerViewController.swift @@ -137,10 +137,34 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { doneButton.image = UIImage(systemName: "checkmark", withConfiguration: navigationSymbolConfiguration) doneButton.accessibilityLabel = configure.doneTitle configureAlbumTitleBackground() + configureAlbumPopupBackground() configureNavigationScrollEdge() } } + @available(iOS 26.0, *) + private func configureAlbumPopupBackground() { + guard let popup = albumPopView.popupView else { return } + let corners = UICornerConfiguration.uniformCorners(radius: .fixed(24)) + popup.backgroundColor = .clear + popup.cornerConfiguration = corners + albumPopView.tableView.backgroundColor = .clear + popArrowImageView.isHidden = true + + let background = UIVisualEffectView(effect: UIGlassEffect(style: .regular)) + background.cornerConfiguration = corners + background.isUserInteractionEnabled = false + background.translatesAutoresizingMaskIntoConstraints = false + popup.insertSubview(background, at: 0) + + NSLayoutConstraint.activate([ + background.leadingAnchor.constraint(equalTo: popup.leadingAnchor), + background.trailingAnchor.constraint(equalTo: popup.trailingAnchor), + background.topAnchor.constraint(equalTo: popup.topAnchor), + background.bottomAnchor.constraint(equalTo: popup.bottomAnchor) + ]) + } + @available(iOS 26.0, *) private func configureAlbumTitleBackground() { let background = UIVisualEffectView(effect: UIGlassEffect(style: .regular)) @@ -183,6 +207,19 @@ class customPhotoPickerViewController: TLPhotosPickerViewController { applyCustomButtons() } + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = super.tableView(tableView, cellForRowAt: indexPath) + if #available(iOS 26.0, *), let albumCell = cell as? TLCollectionTableViewCell { + albumCell.backgroundColor = .clear + albumCell.contentView.backgroundColor = .clear + albumCell.subTitleLabel.textColor = .secondaryLabel + albumCell.thumbImageView.layer.cornerRadius = 10 + albumCell.thumbImageView.layer.cornerCurve = .continuous + albumCell.thumbImageView.clipsToBounds = true + } + return cell + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() if #available(iOS 26.0, *), navigationBar.isTranslucent {