Compare commits

...

25 Commits

Author SHA1 Message Date
Duraid Abdul 372c8ce90b Update LCManager.swift 2021-10-16 17:40:08 -06:00
Duraid Abdul 0d36867f6b Reduced top endpoint padding 2021-09-12 12:58:57 -06:00
Duraid Abdul 3fff6edff0 Revert to cached position on resize 2021-09-12 12:29:03 -06:00
Duraid Abdul 485126dcf7 Update LCManager.swift 2021-09-11 10:02:25 -06:00
Duraid Abdul 245d69679d Merge branch 'main' of https://github.com/duraidabdul/LocalConsole into main 2021-09-06 16:50:03 -06:00
Duraid Abdul 7920272bff Update LCManager.swift 2021-09-06 16:49:56 -06:00
Duraid Abdul 6892a19b0e Update README.md 2021-09-05 12:22:18 -06:00
Duraid Abdul d2d45f8e03 Update LCManager.swift 2021-09-05 12:00:49 -06:00
Duraid Abdul d5a06c013e Update LCManager.swift 2021-09-05 11:49:50 -06:00
Duraid Abdul 3c2683c6bf Update LCManager.swift 2021-09-01 19:04:56 -06:00
Duraid Abdul fd1114802f Update LCManager.swift 2021-09-01 18:53:40 -06:00
Duraid Abdul afe572f4e3 Merge branch 'main' of https://github.com/duraidabdul/LocalConsole into main 2021-09-01 18:35:57 -06:00
Duraid Abdul ceb5ed0a0c Update LCManager.swift 2021-09-01 18:35:54 -06:00
Duraid Abdul 9189fa4173 Update README.md 2021-08-28 18:24:03 -07:00
Duraid Abdul 1e39b362cc Update LCManager.swift 2021-08-28 02:02:22 -07:00
Duraid Abdul 265eaeadad Update LCManager.swift
Code maintenance, fix for grabber long press bug.
2021-08-28 01:49:03 -07:00
Duraid Abdul cf0d3beb76 Update ResizeController.swift 2021-08-27 14:15:28 -07:00
Duraid Abdul ec386069a2 Update LCManager.swift 2021-08-27 14:04:56 -07:00
Duraid Abdul ab36ae5bb8 Update LCManager.swift
Fix keyboard avoidance, improve view states throughout interaction.
2021-08-27 12:39:48 -07:00
Duraid Abdul e6846048d0 Console hiding animation refinements 2021-08-27 01:58:06 -07:00
Duraid Abdul d6d9aad082 @available fix 2021-08-26 23:59:12 -07:00
Duraid Abdul 6723947fe6 Update LCManager.swift 2021-08-26 23:42:41 -07:00
Duraid Abdul 24a7a197b0 Hide Console Interaction
Added interaction and animations for hiding console away
2021-08-26 23:41:53 -07:00
Duraid Abdul ab79c1200f Update LCManager.swift
Fix for some potential issues that could be caused by initializing LCManager off the main queue, and improved the console text view's ability to stick to the bottom if it is scrolled to the bottom.
2021-08-16 11:40:35 -07:00
Duraid Abdul 0ae97b8162 Update LCManager.swift 2021-08-02 12:39:49 -07:00
3 changed files with 421 additions and 122 deletions
+2 -7
View File
@@ -24,10 +24,10 @@ let consoleManager = LCManager.shared
Once prepared, the localConsole can be used throughout your project.
```swift
// Show the console view.
// Activate the console view.
consoleManager.isVisible = true
// Hide the console view.
// Deactivate the console view.
consoleManager.isVisible = false
```
@@ -46,8 +46,3 @@ consoleManager.copy()
// Change the console view font size.
consoleManager.fontSize = 5
```
## **To-Do**
* Screen edge console hiding
* Make console view reactive to landscape/portrait switch
+414 -114
View File
@@ -5,8 +5,6 @@
// Copyright © 2021 Duraid Abdul. All rights reserved.
//
//#if canImport(UIKit)
import UIKit
import SwiftUI
@@ -17,8 +15,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
public static let shared = LCManager()
/// Set the font size. The font can be set to a minimum value of 5.0 and a maximum value of 20.0. The default value is 7.5.
public var fontSize: CGFloat = 7.5 {
/// Set the font size. The font can be set to a minimum value of 5.0 and a maximum value of 20.0. The default value is 8.
public var fontSize: CGFloat = 8 {
didSet {
guard fontSize >= 4 else { fontSize = 4; return }
guard fontSize <= 20 else { fontSize = 20; return }
@@ -46,7 +44,63 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
let defaultConsoleSize = CGSize(width: 228, height: 142)
let defaultConsoleSize = CGSize(width: 240, height: 148)
lazy var borderView = UIView()
var lumaWidthAnchor: NSLayoutConstraint!
var lumaHeightAnchor: NSLayoutConstraint!
lazy var lumaView: LumaView = {
let lumaView = LumaView()
lumaView.foregroundView.backgroundColor = .black
lumaView.layer.cornerRadius = consoleView.layer.cornerRadius
consoleView.addSubview(lumaView)
lumaView.translatesAutoresizingMaskIntoConstraints = false
lumaWidthAnchor = lumaView.widthAnchor.constraint(equalTo: consoleView.widthAnchor)
lumaHeightAnchor = lumaView.heightAnchor.constraint(equalToConstant: consoleView.frame.size.height)
NSLayoutConstraint.activate([
lumaWidthAnchor,
lumaHeightAnchor,
lumaView.centerXAnchor.constraint(equalTo: consoleView.centerXAnchor),
lumaView.centerYAnchor.constraint(equalTo: consoleView.centerYAnchor)
])
return lumaView
}()
lazy var unhideButton: UIButton = {
let button = UIButton()
button.addAction(UIAction(handler: { [self] _ in
UIViewPropertyAnimator(duration: 0.5, dampingRatio: 1) {
consoleView.center = nearestTargetTo(consoleView.center, possibleTargets: possibleEndpoints.dropLast())
}.startAnimation()
grabberMode = false
UserDefaults.standard.set(consoleView.center.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(consoleView.center.y, forKey: "LocalConsole_Y")
}), for: .touchUpInside)
consoleView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalTo: consoleView.widthAnchor),
button.heightAnchor.constraint(equalTo: consoleView.heightAnchor),
button.centerXAnchor.constraint(equalTo: consoleView.centerXAnchor),
button.centerYAnchor.constraint(equalTo: consoleView.centerYAnchor)
])
button.isHidden = true
return button
}()
/// The fixed size of the console view.
lazy var consoleSize = defaultConsoleSize {
@@ -55,22 +109,22 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
// Update text view width.
if consoleView.frame.size.width > ResizeController.kMaxConsoleWidth {
consoleTextView.frame.size.width = ResizeController.kMaxConsoleWidth - 4
consoleTextView.frame.size.width = ResizeController.kMaxConsoleWidth - 2
} else if consoleView.frame.size.width < ResizeController.kMinConsoleWidth {
consoleTextView.frame.size.width = ResizeController.kMinConsoleWidth - 4
consoleTextView.frame.size.width = ResizeController.kMinConsoleWidth - 2
} else {
consoleTextView.frame.size.width = consoleSize.width - 4
consoleTextView.frame.size.width = consoleSize.width - 2
}
// Update text view height.
if consoleView.frame.size.height > ResizeController.kMaxConsoleHeight {
consoleTextView.frame.size.height = ResizeController.kMaxConsoleHeight - 4
consoleTextView.frame.size.height = ResizeController.kMaxConsoleHeight - 2
+ (consoleView.frame.size.height - ResizeController.kMaxConsoleHeight) * 2 / 3
} else if consoleView.frame.size.height < ResizeController.kMinConsoleHeight {
consoleTextView.frame.size.height = ResizeController.kMinConsoleHeight - 4
consoleTextView.frame.size.height = ResizeController.kMinConsoleHeight - 2
+ (consoleView.frame.size.height - ResizeController.kMinConsoleHeight) * 2 / 3
} else {
consoleTextView.frame.size.height = consoleSize.height - 4
consoleTextView.frame.size.height = consoleSize.height - 2
}
consoleTextView.contentOffset.y = consoleTextView.contentSize.height - consoleTextView.bounds.size.height
@@ -86,11 +140,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
var consoleWindow: ConsoleWindow?
// The console needs a parent view controller in order to display context menus.
let viewController = UIViewController()
lazy var viewController = UIViewController()
lazy var consoleView = viewController.view!
/// Text view that displays printed items.
let consoleTextView = InvertedTextView()
lazy var consoleTextView = InvertedTextView()
/// Button that reveals menu.
lazy var menuButton = UIButton()
@@ -99,27 +153,92 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
var scrollLocked = true
/// Feedback generator for the long press action.
let feedbackGenerator = UISelectionFeedbackGenerator()
lazy var feedbackGenerator = UISelectionFeedbackGenerator()
lazy var panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(consolePiPPanner(recognizer:)))
lazy var longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(recognizer:)))
/// Gesture endpoints. Each point represents a corner of the screen. TODO: Handle screen rotation.
var possibleEndpoints: [CGPoint] {
if consoleSize.width < UIScreen.portraitSize.width - 112 {
return [CGPoint(x: consoleSize.width / 2 + 12,
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: consoleSize.width / 2 + 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12),
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
// Four endpoints, one for each corner.
var endpoints = [
// Top endpoints.
CGPoint(x: consoleSize.width / 2 + 12,
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
// Bottom endpoints.
CGPoint(x: consoleSize.width / 2 + 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12),
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
if consoleView.frame.minX <= 0 {
// Left edge endpoints.
endpoints = [endpoints[0], endpoints[2]]
// Left edge hiding endpoints.
if consoleView.center.y < (UIScreen.portraitSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 28,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 28,
y: endpoints[1].y))
}
} else if consoleView.frame.maxX >= UIScreen.portraitSize.width {
// Right edge endpoints.
endpoints = [endpoints[1], endpoints[3]]
// Right edge hiding endpoints.
if consoleView.center.y < (UIScreen.portraitSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 28,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 28,
y: endpoints[1].y))
}
}
return endpoints
} else {
return [CGPoint(x: UIScreen.portraitSize.width / 2,
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: UIScreen.portraitSize.width / 2,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
// Two endpoints, one for the top, one for the bottom..
var endpoints = [CGPoint(x: UIScreen.portraitSize.width / 2,
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: UIScreen.portraitSize.width / 2,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
if consoleView.frame.minX <= 0 {
// Left edge hiding endpoints.
if consoleView.center.y < (UIScreen.portraitSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 28,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 28,
y: endpoints[1].y))
}
} else if consoleView.frame.maxX >= UIScreen.portraitSize.width {
// Right edge hiding endpoints.
if consoleView.center.y < (UIScreen.portraitSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 28,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 28,
y: endpoints[1].y))
}
}
return endpoints
}
}
@@ -129,21 +248,20 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleSize = CGSize(width: UserDefaults.standard.object(forKey: "LocalConsole_Width") as? CGFloat ?? consoleSize.width,
height: UserDefaults.standard.object(forKey: "LocalConsole_Height") as? CGFloat ?? consoleSize.height)
consoleView.backgroundColor = .black
consoleView.layer.shadowRadius = 16
consoleView.layer.shadowOpacity = 0.5
consoleView.layer.shadowOffset = CGSize(width: 0, height: 2)
consoleView.center = possibleEndpoints.first!
consoleView.alpha = 0
consoleView.layer.cornerRadius = 22
consoleView.layer.cornerRadius = 24
consoleView.layer.cornerCurve = .continuous
let borderView = UIView()
let _ = lumaView
borderView.frame = CGRect(x: -1, y: -1,
width: consoleSize.width + 2,
height: consoleSize.height + 2)
width: consoleSize.width + 2,
height: consoleSize.height + 2)
borderView.layer.borderWidth = 1
borderView.layer.borderColor = UIColor(white: 1, alpha: 0.08).cgColor
borderView.layer.cornerRadius = consoleView.layer.cornerRadius + 1
@@ -152,10 +270,10 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleView.addSubview(borderView)
// Configure text view.
consoleTextView.frame = CGRect(x: 2, y: 2, width: consoleSize.width - 4, height: consoleSize.height - 4)
consoleTextView.frame = CGRect(x: 1, y: 1, width: consoleSize.width - 2, height: consoleSize.height - 2)
consoleTextView.isEditable = false
consoleTextView.backgroundColor = .clear
consoleTextView.textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 10, right: 8)
consoleTextView.textContainerInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
consoleTextView.isSelectable = false
consoleTextView.showsVerticalScrollIndicator = false
@@ -179,7 +297,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleView.addGestureRecognizer(longPressRecognizer)
// Prepare menu button.
let diameter = CGFloat(28)
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,
@@ -199,7 +317,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
circle.isUserInteractionEnabled = false
menuButton.addSubview(circle)
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17)))
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)
@@ -209,6 +328,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
menuButton.showsMenuAsPrimaryAction = true
consoleView.addSubview(menuButton)
let _ = unhideButton
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
}
@@ -217,6 +338,21 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
func configureWindow() {
var windowSceneFound = false
// Update console cached based on last-cached origin.
func updateConsoleOrigin() {
snapToCachedEndpoint()
if consoleView.center.x < 0 || consoleView.center.x > UIScreen.portraitSize.width {
grabberMode = true
scrollLocked = !grabberMode
consoleView.layer.removeAllAnimations()
lumaView.layer.removeAllAnimations()
menuButton.layer.removeAllAnimations()
consoleTextView.layer.removeAllAnimations()
}
}
// Configure console window.
func fetchWindowScene() {
let windowScene = UIApplication.shared
@@ -235,11 +371,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleWindow?.addSubview(consoleView)
UIWindow.swizzleStatusBarAppearanceOverride
updateConsoleOrigin()
}
}
fetchWindowScene()
/// Ensures the window is configured (i.e. scene has been found). If not, delay and wait for a scene to prepare itself, then try again.
for i in 1...10 {
@@ -260,6 +396,14 @@ 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)
consoleView.center = cachedConsolePosition // Update console center so possibleEndpoints are calculated correctly.
consoleView.center = nearestTargetTo(cachedConsolePosition, possibleTargets: possibleEndpoints)
}
// MARK: - Public
public var isVisible = false {
@@ -269,9 +413,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if isVisible {
if !isConsoleConfigured {
configureWindow()
configureConsole()
isConsoleConfigured = true
DispatchQueue.main.async { [self] in
configureWindow()
configureConsole()
isConsoleConfigured = true
}
}
commitTextChanges(requestMenuUpdate: true)
@@ -303,7 +449,61 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
private var _hasRelayedOffsetChange = false
var grabberMode: Bool = false {
didSet {
guard oldValue != grabberMode else { return }
if grabberMode {
lumaView.layer.cornerRadius = consoleView.layer.cornerRadius
lumaHeightAnchor.constant = consoleView.frame.size.height
consoleView.layoutIfNeeded()
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1) { [self] in
consoleTextView.alpha = 0
menuButton.alpha = 0
borderView.alpha = 0
}.startAnimation()
UIViewPropertyAnimator(duration: 0.5, dampingRatio: 1) { [self] in
lumaView.foregroundView.alpha = 0
}.startAnimation()
lumaWidthAnchor.constant = -34
lumaHeightAnchor.constant = 96
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
lumaView.layer.cornerRadius = 8
consoleView.layoutIfNeeded()
}.startAnimation(afterDelay: 0.06)
consoleTextView.isUserInteractionEnabled = false
unhideButton.isHidden = false
} else {
lumaHeightAnchor.constant = consoleView.frame.size.height
lumaWidthAnchor.constant = 0
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.layoutIfNeeded()
lumaView.layer.cornerRadius = consoleView.layer.cornerRadius
}.startAnimation()
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1) { [self] in
consoleTextView.alpha = 1
menuButton.alpha = 1
borderView.alpha = 1
}.startAnimation(afterDelay: 0.2)
UIViewPropertyAnimator(duration: 0.65, dampingRatio: 1) { [self] in
lumaView.foregroundView.alpha = 1
}.startAnimation()
consoleTextView.isUserInteractionEnabled = true
unhideButton.isHidden = true
}
}
}
/// Print items to the console view.
public func print(_ items: Any) {
@@ -326,18 +526,25 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
// MARK: - Private
var temporaryKeyboardHeightValueTracker: CGFloat?
// MARK: Handle keyboard show/hide.
private var keyboardHeight: CGFloat? = nil {
didSet {
temporaryKeyboardHeightValueTracker = oldValue
if consoleView.center != possibleEndpoints[0] && consoleView.center != possibleEndpoints[1] {
let nearestTargetPosition = nearestTargetTo(consoleView.center, possibleTargets: possibleEndpoints.suffix(2))
Swift.print(possibleEndpoints.suffix(2))
UIViewPropertyAnimator(duration: 0.55, dampingRatio: 1) {
self.consoleView.center = nearestTargetPosition
}.startAnimation()
}
temporaryKeyboardHeightValueTracker = keyboardHeight
}
}
@@ -394,6 +601,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
dynamicReportTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
var _currentText = currentText
// To optimize performance, only scan the last 2500 characters of text for system report changes.
let range: NSRange = {
if _currentText.count <= 2500 {
return NSMakeRange(0, _currentText.count)
@@ -413,6 +621,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if currentText != _currentText {
currentText = _currentText
} else {
// Invalidate the timer if there is no longer anything to update.
timer.invalidate()
}
}
@@ -453,21 +663,17 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
@objc func toggleLock() {
scrollLocked.toggle()
}
func commitTextChanges(requestMenuUpdate menuUpdateRequested: Bool) {
if consoleTextView.contentOffset.y > consoleTextView.contentSize.height - consoleTextView.bounds.size.height - 20
|| _hasRelayedOffsetChange == false {
if consoleTextView.contentOffset.y > consoleTextView.contentSize.height - consoleTextView.bounds.size.height - 20 {
// Weird, weird fix that makes the scroll view bottom pinning system work.
consoleTextView.isScrollEnabled.toggle()
consoleTextView.isScrollEnabled.toggle()
consoleTextView.pendingOffsetChange = true
_hasRelayedOffsetChange = true
}
consoleTextView.text = currentText
setAttributedText(currentText)
if menuUpdateRequested {
@@ -492,22 +698,22 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
func makeMenu() -> UIMenu {
let copy = UIAction(title: "Copy",
image: UIImage(systemName: "doc.on.doc"), handler: { _ in
self.copy()
})
image: UIImage(systemName: "doc.on.doc"), handler: { _ in
self.copy()
})
let resize = UIAction(title: "Resize Console",
image: UIImage(systemName: "arrow.left.and.right.square"), handler: { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
ResizeController.shared.isActive.toggle()
ResizeController.shared.platterView.reveal()
}
})
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
ResizeController.shared.isActive.toggle()
ResizeController.shared.platterView.reveal()
}
})
let clear = UIAction(title: "Clear Console",
image: UIImage(systemName: "xmark.square"), handler: { _ in
self.clear()
})
self.clear()
})
let consoleActions = UIMenu(title: "", options: .displayInline, children: [clear, resize])
@@ -518,14 +724,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let viewFrames = UIAction(title: debugBordersEnabled ? "Hide View Frames" : "Show View Frames",
image: UIImage(systemName: frameSymbol), handler: { _ in
self.debugBordersEnabled.toggle()
self.menuButton.menu = self.makeMenu()
})
self.debugBordersEnabled.toggle()
self.menuButton.menu = self.makeMenu()
})
let systemReport = UIAction(title: "System Report",
image: UIImage(systemName: "cpu"), handler: { _ in
self.systemReport()
})
image: UIImage(systemName: "cpu"), handler: { _ in
self.systemReport()
})
// Show the right glyph for the current device being used.
let deviceSymbol: String = {
@@ -550,28 +756,35 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}()
let displayReport = UIAction(title: "Display Report",
image: UIImage(systemName: deviceSymbol), handler: { _ in
self.displayReport()
})
image: UIImage(systemName: deviceSymbol), handler: { _ in
self.displayReport()
})
let respring = UIAction(title: "Restart Spring" + "Board",
image: UIImage(systemName: "apps.iphone"), handler: { _ in
guard let window = UIApplication.shared.windows.first else { return }
window.layer.cornerRadius = UIScreen.main.value(forKey: "_displ" + "ayCorn" + "erRa" + "dius") as! CGFloat
window.layer.masksToBounds = true
let animator = UIViewPropertyAnimator(duration: 0.5, dampingRatio: 1) {
window.transform = .init(scaleX: 0.96, y: 0.96)
window.alpha = 0
}
animator.addCompletion { _ in
while true {
window.snapshotView(afterScreenUpdates: false)
}
}
animator.startAnimation()
})
guard let window = UIApplication.shared.windows.first else { return }
window.layer.cornerRadius = UIScreen.main.value(forKey: "_displ" + "ayCorn" + "erRa" + "dius") as! CGFloat
window.layer.masksToBounds = true
UIViewPropertyAnimator(duration: 0.5, dampingRatio: 1) {
window.transform = .init(scaleX: 0.96, y: 0.96)
window.alpha = 0
}.startAnimation()
// Concurrently run these snapshots to decrease the time to crash.
for _ in 0...1000 {
DispatchQueue.global(qos: .default).async {
// This will cause jetsam to terminate SpringBoard.
while true {
window.snapshotView(afterScreenUpdates: false)
}
}
}
})
let debugActions = UIMenu(title: "", options: .displayInline,
children: [UIMenu(title: "Debug", image: UIImage(systemName: "ant"),
children: [viewFrames, systemReport, displayReport, respring])])
@@ -591,6 +804,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
@objc func longPressAction(recognizer: UILongPressGestureRecognizer) {
switch recognizer.state {
case .began:
guard !grabberMode else { return }
feedbackGenerator.selectionChanged()
scrollLocked = false
@@ -598,17 +814,21 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 1.04, y: 1.04)
consoleTextView.alpha = 0.5
menuButton.alpha = 0.5
}.startAnimation()
case .cancelled, .ended:
scrollLocked = true
if !grabberMode { scrollLocked = true }
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.5) { [self] in
consoleView.transform = .init(scaleX: 1, y: 1)
consoleView.transform = .identity
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleTextView.alpha = 1
if !grabberMode {
consoleTextView.alpha = 1
menuButton.alpha = 1
}
}.startAnimation()
default: break
}
@@ -628,8 +848,16 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
switch recognizer.state {
case .changed:
consoleView.center.x = initialViewLocation.x + translation.x
consoleView.center.y = initialViewLocation.y + translation.y
UIViewPropertyAnimator(duration: 0.175, dampingRatio: 1) { [self] in
consoleView.center = CGPoint(x: initialViewLocation.x + translation.x,
y: initialViewLocation.y + translation.y)
}.startAnimation()
if consoleView.frame.maxX > 30 && consoleView.frame.minX < UIScreen.portraitSize.width - 30 {
grabberMode = false
} else {
grabberMode = true
}
case .ended, .cancelled:
@@ -648,27 +876,31 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
dy: relativeVelocity(forVelocity: velocity.y, from: consoleView.center.y, to: nearestTargetPosition.y)
)
let timingParameters = UISpringTimingParameters(damping: 1, response: 0.4, initialVelocity: relativeInitialVelocity)
let timingParameters = UISpringTimingParameters(damping: 0.85, response: 0.45, initialVelocity: relativeInitialVelocity)
let positionAnimator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
positionAnimator.addAnimations { [self] in
consoleView.center = nearestTargetPosition
}
positionAnimator.startAnimation()
UserDefaults.standard.set(nearestTargetPosition.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(nearestTargetPosition.y, forKey: "LocalConsole_Y")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.grabberMode = nearestTargetPosition.x < 0 || nearestTargetPosition.x > UIScreen.portraitSize.width
self.scrollLocked = !self.grabberMode
}
default: break
}
}
// Animate touch down.
func consolePiPTouchDown() {
UIViewPropertyAnimator(duration: 1, dampingRatio: 0.5) { [self] in
consoleView.transform = .init(scaleX: 0.96, y: 0.96)
}.startAnimation()
guard !grabberMode else { return }
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
if !scrollLocked {
consoleView.backgroundColor = #colorLiteral(red: 0.1331297589, green: 0.1331297589, blue: 0.1331297589, alpha: 1)
}
UIViewPropertyAnimator(duration: 1.25, dampingRatio: 0.5) { [self] in
consoleView.transform = .init(scaleX: 0.95, y: 0.95)
}.startAnimation()
}
@@ -679,11 +911,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleTextView.alpha = 1
}.startAnimation()
UIViewPropertyAnimator(duration: 0.75, dampingRatio: 1) { [self] in
consoleView.backgroundColor = .black
if !grabberMode {
consoleTextView.alpha = 1
if !ResizeController.shared.isActive {
menuButton.alpha = 1
}
}
}.startAnimation()
}
@@ -696,15 +929,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
switch recognizer.state {
case .began:
consolePiPTouchDown()
case .cancelled:
consolePiPTouchUp()
case .changed:
break
case .ended:
consolePiPTouchUp()
case .failed:
consolePiPTouchUp()
case .possible:
case .ended, .cancelled, .possible, .failed:
consolePiPTouchUp()
@unknown default:
break
@@ -747,7 +974,7 @@ extension UIView {
let swizzledMethod = class_getInstanceMethod(UIView.self, #selector(swizzled_layoutSubviews)) else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@objc func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
@@ -772,7 +999,80 @@ extension UIWindow {
}
}
//#endif
class LumaView: UIView {
lazy var visualEffectView: UIView = {
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Frameworks/Material" + "Kit." + "framework")!.load()
let Pill = NSClassFromString("MT" + "Luma" + "Dodge" + "Pill" + "View") as! UIView.Type
let pillView = Pill.init()
enum Style: Int {
case none = 0
case thin = 1
case gray = 2
case black = 3
case white = 4
}
enum BackgroundLuminance: Int {
case unknown = 0
case dark = 1
case light = 2
}
pillView.setValue(2, forKey: "style")
pillView.setValue(1, forKey: "background" + "Luminance")
pillView.perform(NSSelectorFromString("_" + "update" + "Style"))
addSubview(pillView)
pillView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
pillView.leadingAnchor.constraint(equalTo: leadingAnchor),
pillView.trailingAnchor.constraint(equalTo: trailingAnchor),
pillView.topAnchor.constraint(equalTo: topAnchor),
pillView.bottomAnchor.constraint(equalTo: bottomAnchor)
])
return pillView
}()
lazy var foregroundView: UIView = {
let view = UIView()
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: leadingAnchor),
view.trailingAnchor.constraint(equalTo: trailingAnchor),
view.topAnchor.constraint(equalTo: topAnchor),
view.bottomAnchor.constraint(equalTo: bottomAnchor)
])
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
let _ = visualEffectView
let _ = foregroundView
visualEffectView.isUserInteractionEnabled = false
foregroundView.isUserInteractionEnabled = false
layer.cornerCurve = .continuous
clipsToBounds = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class InvertedTextView: UITextView {
@@ -781,7 +1081,7 @@ class InvertedTextView: UITextView {
// Thanks to WWDC21 Lab!
override func layoutSubviews() {
super.layoutSubviews()
if panGestureRecognizer.numberOfTouches == 0 && pendingOffsetChange {
contentOffset.y = contentSize.height - bounds.size.height
} else {
+5 -1
View File
@@ -170,7 +170,7 @@ class ResizeController {
LCManager.shared.consoleView.layer.shadowOpacity = 0.5
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
LCManager.shared.consoleView.center = LCManager.shared.possibleEndpoints.first!
LCManager.shared.snapToCachedEndpoint()
// Update grabbers (layout constraints)
LCManager.shared.consoleWindow?.layoutIfNeeded()
@@ -236,6 +236,7 @@ class ResizeController {
}
}()
LCManager.shared.lumaHeightAnchor.constant = resolvedHeight
LCManager.shared.consoleSize.height = resolvedHeight
LCManager.shared.consoleView.center.y = consoleCenterPoint.y
@@ -243,9 +244,11 @@ class ResizeController {
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7) {
if LCManager.shared.consoleSize.height > maxHeight {
LCManager.shared.consoleSize.height = maxHeight
LCManager.shared.lumaHeightAnchor.constant = maxHeight
}
if LCManager.shared.consoleSize.height < minHeight {
LCManager.shared.consoleSize.height = minHeight
LCManager.shared.lumaHeightAnchor.constant = minHeight
}
LCManager.shared.consoleView.center.y = self.consoleCenterPoint.y
@@ -475,6 +478,7 @@ class PlatterView: UIView {
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) {
LCManager.shared.consoleSize = LCManager.shared.defaultConsoleSize
LCManager.shared.lumaHeightAnchor.constant = LCManager.shared.defaultConsoleSize.height
LCManager.shared.consoleView.center = ResizeController.shared.consoleCenterPoint
LCManager.shared.consoleWindow?.layoutIfNeeded()
}.startAnimation()