From 37847a2f9fb60317114cf18aaf77fa900acc4e95 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Thu, 2 Feb 2023 23:36:47 -0500 Subject: [PATCH 01/27] Fix accent color circles not showing on iOS 15 --- .../Preferences/AppearancePrefsView.swift | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Tusker/Screens/Preferences/AppearancePrefsView.swift b/Tusker/Screens/Preferences/AppearancePrefsView.swift index b0780279..be3ac44e 100644 --- a/Tusker/Screens/Preferences/AppearancePrefsView.swift +++ b/Tusker/Screens/Preferences/AppearancePrefsView.swift @@ -28,6 +28,21 @@ struct AppearancePrefsView : View { }) { Preferences.shared.avatarStyle = $0 ? .circle : .roundRect } + + private let accentColorsAndImages: [(Preferences.AccentColor, UIImage?)] = Preferences.AccentColor.allCases.map { color in + var image: UIImage? + if let color = color.color { + if #available(iOS 16.0, *) { + image = UIImage(systemName: "circle.fill")!.withTintColor(color, renderingMode: .alwaysTemplate).withRenderingMode(.alwaysOriginal) + } else { + image = UIGraphicsImageRenderer(size: CGSize(width: 20, height: 20)).image { context in + color.setFill() + context.cgContext.fillEllipse(in: CGRect(x: 0, y: 0, width: 20, height: 20)) + } + } + } + return (color, image) + } var body: some View { List { @@ -48,12 +63,12 @@ struct AppearancePrefsView : View { } Picker(selection: accentColor, label: Text("Accent Color")) { - ForEach(Preferences.AccentColor.allCases, id: \.rawValue) { color in + ForEach(accentColorsAndImages, id: \.0.rawValue) { (color, image) in HStack { Text(color.name) - if let color = color.color { + if let image { Spacer() - Image(uiImage: UIImage(systemName: "circle.fill")!.withTintColor(color, renderingMode: .alwaysTemplate).withRenderingMode(.alwaysOriginal)) + Image(uiImage: image) } } .tag(color) From 597dd56032e2fd6c51cbdb1f788cf9a809efe4ba Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 3 Feb 2023 18:30:37 -0500 Subject: [PATCH 02/27] Fix crash on split VC collapse/expand while in explore tab Also fix iPad explore VC resetting when leaving/reopening the app Closes #351 --- Tusker/Screens/Main/MainSplitViewController.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Tusker/Screens/Main/MainSplitViewController.swift b/Tusker/Screens/Main/MainSplitViewController.swift index 30329717..013845be 100644 --- a/Tusker/Screens/Main/MainSplitViewController.swift +++ b/Tusker/Screens/Main/MainSplitViewController.swift @@ -285,7 +285,9 @@ extension MainSplitViewController: UISplitViewControllerDelegate { // Search screen has special considerations, all others can be transferred directly. if tabNavigationStack.count == 1 || ((tabNavigationStack.first as? ExploreViewController)?.searchController?.isActive ?? false) { exploreItem = .explore - let searchVC = SearchViewController(mastodonController: mastodonController) + // reuse the existing VC, if there is one + let searchVC = getOrCreateNavigationStack(item: .explore).first! as! SearchViewController + // load the view so that the search controller is accessible searchVC.loadViewIfNeeded() let explore = tabNavigationStack.first as! ExploreViewController if let exploreSearchControler = explore.searchController, @@ -316,7 +318,10 @@ extension MainSplitViewController: UISplitViewControllerDelegate { case is ProfileDirectoryViewController: exploreItem = .profileDirectory default: - fatalError("unhandled second-level explore screen: \(tabNavigationStack[1])") + // transfer the navigation stack prepending, the existing explore VC + // if there was other stuff on the explore stack, it will get discarded + toPrepend = getOrCreateNavigationStack(item: .explore).first! + exploreItem = .explore } } transferNavigationStack(from: tabNavController, to: exploreItem!, skipFirst: skipFirst, prepend: toPrepend) From 91123fd24af5684eb3b082ae497e3562f26acaa7 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 4 Feb 2023 11:10:01 -0500 Subject: [PATCH 03/27] Make username label on profile copyable --- Tusker.xcodeproj/project.pbxproj | 4 ++ Tusker/Views/CopyableLable.swift | 50 +++++++++++++++++++ .../Profile Header/ProfileHeaderView.xib | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 Tusker/Views/CopyableLable.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 7f4562a1..63b979d8 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -321,6 +321,7 @@ D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; }; D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */; }; D6D706A72948D4D0000827ED /* TimlineState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D706A62948D4D0000827ED /* TimlineState.swift */; }; + D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.swift */; }; D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */; }; D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A44273D6C5700386A6C /* GIFImageView.swift */; }; D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; }; @@ -734,6 +735,7 @@ D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingSegmentedControl.swift; sourceTree = ""; }; D6D706A62948D4D0000827ED /* TimlineState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimlineState.swift; sourceTree = ""; }; D6D706A829498C82000827ED /* Tusker.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Tusker.xcconfig; sourceTree = ""; }; + D6D9498E298EB79400C59229 /* CopyableLable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLable.swift; sourceTree = ""; }; D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentImage.swift; sourceTree = ""; }; D6DD2A44273D6C5700386A6C /* GIFImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFImageView.swift; sourceTree = ""; }; D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = ""; }; @@ -1414,6 +1416,7 @@ D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */, D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */, D620483523D38075008A63EF /* ContentTextView.swift */, + D6D9498E298EB79400C59229 /* CopyableLable.swift */, D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */, D6969E9F240C8384002843CE /* EmojiLabel.swift */, D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */, @@ -2117,6 +2120,7 @@ D663626221360B1900C9CBA2 /* Preferences.swift in Sources */, D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */, D627943B23A55BA600D38C68 /* NavigableTableViewCell.swift in Sources */, + D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */, D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */, D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */, D653F411267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift in Sources */, diff --git a/Tusker/Views/CopyableLable.swift b/Tusker/Views/CopyableLable.swift new file mode 100644 index 00000000..23d070e1 --- /dev/null +++ b/Tusker/Views/CopyableLable.swift @@ -0,0 +1,50 @@ +// +// CopyableLable.swift +// Tusker +// +// Created by Shadowfacts on 2/4/23. +// Copyright © 2023 Shadowfacts. All rights reserved. +// + +import UIKit + +class CopyableLable: UILabel { + + private var _editMenuInteraction: Any! + @available(iOS 16.0, *) + private var editMenuInteraction: UIEditMenuInteraction { + get { _editMenuInteraction as! UIEditMenuInteraction } + set { _editMenuInteraction = newValue } + } + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + commonInit() + } + + private func commonInit() { + if #available(iOS 16.0, *) { + editMenuInteraction = UIEditMenuInteraction(delegate: nil) + addInteraction(editMenuInteraction) + isUserInteractionEnabled = true + addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(longPressed))) + } + } + + override func copy(_ sender: Any?) { + UIPasteboard.general.string = text + } + + @available(iOS 16.0, *) + @objc private func longPressed(_ recognizer: UILongPressGestureRecognizer) { + if recognizer.state == .began { + editMenuInteraction.presentEditMenu(with: UIEditMenuConfiguration(identifier: nil, sourcePoint: CGPoint(x: bounds.midX, y: bounds.midY))) + } + } + +} diff --git a/Tusker/Views/Profile Header/ProfileHeaderView.xib b/Tusker/Views/Profile Header/ProfileHeaderView.xib index b1b73820..23a651ed 100644 --- a/Tusker/Views/Profile Header/ProfileHeaderView.xib +++ b/Tusker/Views/Profile Header/ProfileHeaderView.xib @@ -111,7 +111,7 @@ -