Compare commits

..

3 Commits

Author SHA1 Message Date
Duraid Abdul 2c5d5e018a Fix share sheet crash 2022-12-04 16:19:33 -07:00
Duraid Abdul 6413eb8a81 Add a custom menu to the console’s menu 2022-11-21 23:44:08 -07:00
Duraid Abdul 1dcb6e3a57 Fix build failure 2022-10-16 19:21:53 -06:00
+130 -48
View File
@@ -145,7 +145,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lazy var consoleTextView = InvertedTextView()
/// Button that reveals menu.
lazy var menuButton = UIButton()
lazy var menuButton = ConsoleMenuButton()
/// Tracks whether the PiP console is in text view scroll mode or pan mode.
var scrollLocked = true
@@ -278,9 +278,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let borderWidth = 2 - 1 / consoleView.traitCollection.displayScale
borderView.frame = CGRect(x: -borderWidth, y: -borderWidth,
width: consoleSize.width + 2 * borderWidth,
height: consoleSize.height + 2 * borderWidth)
borderView.frame = CGRect(
x: -borderWidth, y: -borderWidth,
width: consoleSize.width + 2 * borderWidth,
height: consoleSize.height + 2 * borderWidth
)
borderView.layer.borderWidth = borderWidth
borderView.layer.borderColor = UIColor(white: 1, alpha: 0.08).cgColor
borderView.layer.cornerRadius = consoleView.layer.cornerRadius + 1
@@ -319,10 +321,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let diameter = CGFloat(30)
// This tuned button frame is used to adjust where the menu appears.
menuButton = UIButton(frame: CGRect(x: consoleView.bounds.width - 44,
y: consoleView.bounds.height - 36,
width: 44,
height: 36 + 4 /*Offests the context menu by the desired amount*/))
menuButton.frame = CGRect(
x: consoleView.bounds.width - 44,
y: consoleView.bounds.height - 36,
width: 44,
height: 36 + 4 /*Offests the context menu by the desired amount*/
)
menuButton.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin]
let circleFrame = CGRect(
@@ -336,13 +340,17 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
circle.isUserInteractionEnabled = false
menuButton.addSubview(circle)
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)))
let ellipsisImage = UIImageView(
image: UIImage(
systemName: "ellipsis",
withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)
)
)
ellipsisImage.frame.size = circle.bounds.size
ellipsisImage.contentMode = .center
circle.addSubview(ellipsisImage)
menuButton.tintColor = UIColor(white: 1, alpha: 0.75)
menuButton.tintColor = UIColor(white: 1, alpha: 0.8)
menuButton.menu = makeMenu()
menuButton.showsMenuAsPrimaryAction = true
consoleView.addSubview(menuButton)
@@ -355,7 +363,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
/// Adds a consoleViewController to the app's main window.
func configureConsoleViewController() {
var windowSceneFound = false
var windowFound = false
// Update console cached based on last-cached origin.
func updateConsoleOrigin() {
@@ -373,25 +381,41 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
// Configure console window.
func fetchWindowScene() {
let windowScene = UIApplication.shared
.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first
func fetchWindow() -> UIWindow? {
if #available(iOS 15.0, *) {
let windowScene = UIApplication.shared
.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first
if let windowScene = windowScene as? UIWindowScene, let keyWindow = windowScene.keyWindow {
return keyWindow
}
return nil
} else {
return UIApplication.shared.windows.first
}
if let windowScene = windowScene as? UIWindowScene, let keyWindow = windowScene.keyWindow {
windowSceneFound = true
SwizzleTool().swizzleContextMenuReverseOrder()
consoleViewController.view = PassthroughView()
consoleViewController.view.addSubview(consoleView)
keyWindow.addSubview(consoleViewController.view)
consoleViewController.view.frame = keyWindow.bounds
consoleViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
updateConsoleOrigin()
}
func addConsoleToWindow(window: UIWindow) {
window.addSubview(consoleViewController.view)
window.rootViewController?.addChild(consoleViewController)
consoleViewController.view = PassthroughView()
consoleViewController.view.addSubview(consoleView)
consoleViewController.view.frame = window.bounds
consoleViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
updateConsoleOrigin()
SwizzleTool().swizzleContextMenuReverseOrder()
// Ensure console view always stays above other views.
SwizzleTool().swizzleDidAddSubview {
window.bringSubviewToFront(self.consoleViewController.view)
}
}
@@ -401,9 +425,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [self] in
guard !windowSceneFound else { return }
guard !windowFound else { return }
fetchWindowScene()
if let window = fetchWindow() {
windowFound = true
addConsoleToWindow(window: window)
}
if isVisible {
isVisible = false
@@ -415,8 +442,10 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
func snapToCachedEndpoint() {
let cachedConsolePosition = CGPoint(x: UserDefaults.standard.object(forKey: "LocalConsole.X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole.Y") as? CGFloat ?? possibleEndpoints.first!.y)
let cachedConsolePosition = CGPoint(
x: UserDefaults.standard.object(forKey: "LocalConsole.X") as? CGFloat ?? possibleEndpoints.first!.x,
y: UserDefaults.standard.object(forKey: "LocalConsole.Y") as? CGFloat ?? possibleEndpoints.first!.y
)
consoleView.center = cachedConsolePosition // Update console center so possibleEndpoints are calculated correctly.
consoleView.center = nearestTargetTo(cachedConsolePosition, possibleTargets: possibleEndpoints)
@@ -467,8 +496,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
// Specify a UIMenu or UIAction to be included in the console's main menu.
public var menu: UIMenuElement? = nil {
didSet {
menuButton.menu = makeMenu()
}
}
var grabberMode: Bool = false {
didSet {
guard oldValue != grabberMode else { return }
@@ -490,7 +525,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lumaWidthAnchor.constant = -34
lumaHeightAnchor.constant = 96
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
lumaView.layer.cornerRadius = 9
lumaView.layer.cornerRadius = 10
consoleView.layoutIfNeeded()
}.startAnimation(afterDelay: 0.06)
@@ -813,13 +848,22 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
public var showAllUserDefaultsKeys = false
func makeMenu() -> UIMenu {
let share = UIAction(title: "Share Text...", image: UIImage(systemName: "square.and.arrow.up")) { _ in
let activityViewController = UIActivityViewController(
activityItems: [self.consoleTextView.text ?? ""],
applicationActivities: nil
)
self.consoleViewController.present(activityViewController, animated: true)
}
let share: UIAction = {
// Something here causes a crash < iOS 15. Fall back to copy text for iOS 15 and below.
if #available(iOS 16, *) {
return UIAction(title: "Share Text...", image: UIImage(systemName: "square.and.arrow.up")) { _ in
let activityViewController = UIActivityViewController(
activityItems: [self.consoleTextView.text ?? ""],
applicationActivities: nil
)
self.consoleViewController.present(activityViewController, animated: true)
}
} else {
return UIAction(title: "Copy Text", image: UIImage(systemName: "doc.on.doc")) { _ in
UIPasteboard.general.string = self.consoleTextView.text
}
}
}()
let resize = UIAction(title: "Resize Console", image: UIImage(systemName: "arrow.up.backward.and.arrow.down.forward")) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
@@ -871,9 +915,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}()
if keys.isEmpty {
actions.append(UIAction(title: "No Entries",
image: nil, attributes: .disabled, handler: { _ in }
))
actions.append(
UIAction(title: "No Entries", attributes: .disabled, handler: { _ in })
)
} else {
for key in keys.sorted(by: { $0.lowercased() < $1.lowercased() }) {
@@ -1046,7 +1090,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
} else {
menuContent.append(UIMenu(title: "", options: .displayInline, children: [resize]))
}
menuContent.append(debugMenu)
if let customMenu = menu {
menuContent.append(customMenu)
}
if consoleTextView.text != "" {
menuContent.append(UIMenu(title: "", options: .displayInline, children: [clear]))
}
@@ -1227,6 +1276,20 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
/// Custom button that pauses console window swizzling to allow the console menu's presenting view controller to remain the top view controller.
class ConsoleMenuButton: UIButton {
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator)
SwizzleTool.pauseDidAddSubviewSwizzledClosure = true
}
override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willEndFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
SwizzleTool.pauseDidAddSubviewSwizzledClosure = false
}
}
// Custom view that is passes touches .
class PassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@@ -1279,7 +1342,6 @@ class SwizzleTool: NSObject {
}
@objc func swizzled_reverses_Action_Order() -> Bool {
if let menu = self.value(forKey: "displayed" + "Menu") as? UIMenu,
menu.title == "Debug" || menu.title == "User" + "Defaults" {
return false
@@ -1291,8 +1353,28 @@ class SwizzleTool: NSObject {
return false
}
}
static var swizzledDidAddSubviewClosure: (() -> Void)?
static var pauseDidAddSubviewSwizzledClosure: Bool = false
func swizzleDidAddSubview(_ closure: @escaping () -> Void) {
guard let originalMethod = class_getInstanceMethod(UIWindow.self, #selector(UIWindow.didAddSubview(_:))),
let swizzledMethod = class_getInstanceMethod(SwizzleTool.self, #selector(swizzled_did_add_subview(_:)))
else { Swift.print("Swizzle Error Occurred"); return }
method_exchangeImplementations(originalMethod, swizzledMethod)
Self.swizzledDidAddSubviewClosure = closure
}
@objc func swizzled_did_add_subview(_ subview: UIView) {
guard !Self.pauseDidAddSubviewSwizzledClosure else { return }
if let closure = Self.swizzledDidAddSubviewClosure {
closure()
}
}
}
class LumaView: UIView {
lazy var visualEffectView: UIView = {