Compare commits

..

9 Commits

Author SHA1 Message Date
Duraid Abdul a7b95a4379 Update LCManager.swift 2022-01-26 02:18:26 -08:00
Duraid Abdul 3efe25f804 Update LCManager.swift 2022-01-25 17:15:30 -08:00
Duraid Abdul 0a3e28b28f Animation Improvements 2022-01-24 23:09:49 -08:00
Duraid Abdul 009fab95be UserDefaults Support
Signed-off-by: Duraid Abdul <duraidabdul@users.noreply.github.com>
2022-01-24 19:06:49 -08:00
Duraid Abdul a1275a7f49 Update LCManager.swift 2022-01-22 00:09:49 -08:00
Duraid Abdul e08d439d2b Various Improvements 2022-01-22 00:06:35 -08:00
Duraid Abdul da9d78f559 Merge branch 'main' of https://github.com/duraidabdul/LocalConsole 2022-01-21 12:47:53 -08:00
Duraid Abdul 50e4ce4e03 Update LCManager.swift 2022-01-21 12:47:50 -08:00
Duraid Abdul ae73be37b4 Update README.md 2022-01-17 12:08:39 -08:00
3 changed files with 352 additions and 80 deletions
+1 -1
View File
@@ -21,7 +21,7 @@ let consoleManager = LCManager.shared
```
## **Usage**
Once prepared, the localConsole can be used throughout your project.
Once prepared, the consoleManager can be used throughout your project.
```swift
// Activate the console view.
+325 -68
View File
@@ -82,8 +82,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.startAnimation()
grabberMode = false
UserDefaults.standard.set(consoleView.center.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(consoleView.center.y, forKey: "LocalConsole_Y")
UserDefaults.standard.set(consoleView.center.x, forKey: "LocalConsole.X")
UserDefaults.standard.set(consoleView.center.y, forKey: "LocalConsole.Y")
}), for: .touchUpInside)
consoleView.addSubview(button)
@@ -131,8 +131,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
// TODO: Snap to nearest position.
UserDefaults.standard.set(consoleSize.width, forKey: "LocalConsole_Width")
UserDefaults.standard.set(consoleSize.height, forKey: "LocalConsole_Height")
UserDefaults.standard.set(consoleSize.width, forKey: "LocalConsole.Width")
UserDefaults.standard.set(consoleSize.height, forKey: "LocalConsole.Height")
}
}
@@ -267,8 +267,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lazy var initialViewLocation: CGPoint = .zero
func configureConsole() {
consoleSize = CGSize(width: UserDefaults.standard.object(forKey: "LocalConsole_Width") as? CGFloat ?? consoleSize.width,
height: UserDefaults.standard.object(forKey: "LocalConsole_Height") as? CGFloat ?? consoleSize.height)
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.layer.shadowRadius = 16
@@ -312,7 +312,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let tapRecognizer = UITapStartEndGestureRecognizer(target: self, action: #selector(consolePiPTapStartEnd(recognizer:)))
tapRecognizer.delegate = self
longPressRecognizer.minimumPressDuration = 0.1
longPressRecognizer.minimumPressDuration = 0.3
consoleView.addGestureRecognizer(panRecognizer)
consoleView.addGestureRecognizer(tapRecognizer)
@@ -398,6 +398,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
viewController.view.addSubview(consoleView)
UIWindow.swizzleStatusBarAppearanceOverride
SwizzleTool().swizzleContextMenuReverseOrder()
updateConsoleOrigin()
}
@@ -424,8 +425,8 @@ 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)
@@ -532,12 +533,34 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
var hasShortened = false
public var isCharacterLimitDisabled = false
public var isCharacterLimitWarningDisabled = false
/// Print items to the console view.
public func print(_ items: Any) {
if currentText == "" {
currentText = "\(items)"
let _currentText: String = {
if currentText == "" {
return "\(items)"
} else {
return currentText + "\n\(items)"
}
}()
// Cut down string if it exceeds 50,000 characters to keep text view running smoothly.
if _currentText.count > 50000 && !isCharacterLimitDisabled {
if !hasShortened && !isCharacterLimitWarningDisabled {
hasShortened = true
Swift.print("LocalConsole's content has exceeded 50,000 characters.\nTo maintain performance, LCManager cuts down the beginning of the printed content. To disable this behaviour, set LCManager.shared.isCharacterLimitDisabled to true.\nTo disable this warning, set LCManager.shared.isCharacterLimitWarningDisabled = true.")
}
let shortenedString = String(_currentText.suffix(50000))
currentText = shortenedString.stringAfterFirstOccurenceOf(delimiter: "\n") ?? shortenedString
} else {
currentText = currentText + "\n\(items)"
currentText = _currentText
}
}
@@ -615,15 +638,23 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
var dynamicReportTimer: Timer? {
willSet { dynamicReportTimer?.invalidate() }
willSet {
timerInvalidationCounter = 0
dynamicReportTimer?.invalidate()
}
}
var timerInvalidationCounter = 0
func systemReport() {
DispatchQueue.main.async { [self] in
if currentText != "" { print("\n") }
dynamicReportTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
guard consoleTextView.panGestureRecognizer.numberOfTouches == 0 else { return }
var _currentText = currentText
// To optimize performance, only scan the last 2500 characters of text for system report changes.
@@ -645,10 +676,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if currentText != _currentText {
currentText = _currentText
timerInvalidationCounter = 0
} else {
// Invalidate the timer if there is no longer anything to update.
timer.invalidate()
timerInvalidationCounter += 1
// It has been 2 seconds and values have not changed.
if timerInvalidationCounter == 2 {
// Invalidate the timer if there is no longer anything to update.
dynamicReportTimer = nil
}
}
}
@@ -720,6 +760,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleTextView.attributedText = NSAttributedString(string: string, attributes: attributes)
}
// Displays all UserDefaults keys, including unneeded keys that are included by default.
public var showAllUserDefaultsKeys = false
func makeMenu() -> UIMenu {
let copy = UIAction(title: "Copy",
@@ -751,10 +794,127 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let consoleActions = UIMenu(title: "", options: .displayInline, children: [clear, resize])
var frameSymbol = "rectangle.3.offgrid"
var debugActions: [UIMenuElement] = []
if #available(iOS 15, *) {
frameSymbol = "square.inset.filled"
let deferredUserDefaultsList = UIDeferredMenuElement.uncached { completion in
var actions: [UIAction] = []
let keys: [String] = {
if self.showAllUserDefaultsKeys {
return UserDefaults.standard.dictionaryRepresentation().map { $0.key }
}
// Show keys the developer has added to the app (+ LocalConsole keys), excluding all of Apple's keys.
if let bundle: String = Bundle.main.bundleIdentifier {
let preferencePath: String = NSHomeDirectory() + "/Library/Preferences/\(bundle).plist"
let _keys = NSDictionary(contentsOfFile: preferencePath)?.allKeys as! [String]
return _keys.filter {
!$0.contains("LocalConsole.")
}
}
return []
}()
if keys.isEmpty {
actions.append(UIAction(title: "No Entries",
image: nil, attributes: .disabled, handler: { _ in }
))
} else {
for key in keys.sorted(by: { $0.lowercased() < $1.lowercased() }) {
// Old LocalConsole Key Cleanup
guard !key.contains("LocalConsole_") else {
UserDefaults.standard.removeObject(forKey: key)
continue
}
if let value = UserDefaults.standard.value(forKey: key) {
let action = UIAction(title: key, image: nil) { _ in
let alertController = UIAlertController(title: key,
message: nil,
preferredStyle: .alert)
let headerParagraphStyle = NSMutableParagraphStyle()
headerParagraphStyle.paragraphSpacing = 6
let contentParagraphStyle = NSMutableParagraphStyle()
let attributes: [NSAttributedString.Key: Any] = [
.paragraphStyle: contentParagraphStyle,
.foregroundColor: UIColor.label,
.font: UIFont.systemFont(ofSize: 13, weight: .semibold, design: .monospaced)
]
let attributedTitle: NSMutableAttributedString = {
let attributedString = NSMutableAttributedString(string: "Key\n" + key, attributes: attributes)
attributedString.addAttributes(
[NSAttributedString.Key.foregroundColor : UIColor.label.withAlphaComponent(0.5),
NSAttributedString.Key.paragraphStyle : headerParagraphStyle],
range: NSRange(location: 0, length: 3))
return attributedString
}()
let attributedMessage: NSMutableAttributedString = {
let attributedString = NSMutableAttributedString(string: "\nValue\n" + "\(value)", attributes: attributes)
attributedString.addAttributes(
[NSAttributedString.Key.foregroundColor : UIColor.label.withAlphaComponent(0.5),
NSAttributedString.Key.paragraphStyle : headerParagraphStyle],
range: NSRange(location: 0, length: 7))
return attributedString
}()
alertController.setValue(attributedTitle, forKey: "attributedTitle")
alertController.setValue(attributedMessage, forKey: "attributedMessage")
alertController.addAction(UIAlertAction(title: "Copy Value", style: .default, handler: { _ in
UIPasteboard.general.string = "\(value)"
}))
alertController.addAction(UIAlertAction(title: "Clear Value", style: .destructive, handler: { _ in
UserDefaults.standard.removeObject(forKey: key)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in
}))
self.viewController.present(alertController,
animated: true)
}
action.subtitle = "\(value)"
actions.append(action)
}
}
actions.append(
UIAction(title: "Clear Defaults",
image: UIImage(systemName: "trash"), attributes: .destructive, handler: { _ in
keys.forEach {
UserDefaults.standard.removeObject(forKey: $0)
}
})
)
}
completion(actions)
}
let userDefaults = UIMenu(title: "UserDefaults", image: UIImage(systemName: "doc.badge.gearshape"), children: [deferredUserDefaultsList])
debugActions.append(userDefaults)
}
let viewFrames = UIAction(title: debugBordersEnabled ? "Hide View Frames" : "Show View Frames",
image: UIImage(systemName: frameSymbol), handler: { _ in
self.debugBordersEnabled.toggle()
@@ -818,9 +978,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
})
let debugActions = UIMenu(title: "", options: .displayInline,
debugActions.append(contentsOf: [viewFrames, systemReport, displayReport, respring])
let debugMenu = UIMenu(title: "", options: .displayInline,
children: [UIMenu(title: "Debug", image: UIImage(systemName: "ant"),
children: [viewFrames, systemReport, displayReport, respring])])
children: debugActions)])
var menuContent: [UIMenuElement] = []
@@ -829,11 +991,13 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
} else {
menuContent.append(resize)
}
menuContent.append(debugActions)
menuContent.append(debugMenu)
return UIMenu(title: "", children: menuContent)
}
var consolePiPPopAnimator: UIViewPropertyAnimator?
@objc func longPressAction(recognizer: UILongPressGestureRecognizer) {
switch recognizer.state {
case .began:
@@ -844,8 +1008,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
scrollLocked = false
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consolePiPPopAnimator = UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 1.04, y: 1.04)
}
consolePiPPopAnimator?.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleTextView.alpha = 0.5
menuButton.alpha = 0.5
}.startAnimation()
@@ -853,7 +1021,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if !grabberMode { scrollLocked = true }
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.5) { [self] in
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.6) { [self] in
consoleView.transform = .identity
}.startAnimation()
@@ -867,17 +1035,23 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
let consolePiPPanner_frameRateRequest = FrameRateRequest()
var consolePiPPanner_frameRateRequestID: UUID?
@objc func consolePiPPanner(recognizer: UIPanGestureRecognizer) {
if recognizer.state == .began {
consolePiPPanner_frameRateRequest.isActive = true
if #available(iOS 15, *) {
consolePiPPanner_frameRateRequestID = UUID()
FrameRateRequest.shared.activate(id: consolePiPPanner_frameRateRequestID!)
}
initialViewLocation = consoleView.center
}
guard !scrollLocked else { return }
guard !scrollLocked else {
isPressed = false
return
}
let translation = recognizer.translation(in: consoleView.superview)
let velocity = recognizer.velocity(in: consoleView.superview)
@@ -894,8 +1068,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
case .ended, .cancelled:
consolePiPPanner_frameRateRequest.isActive = false
FrameRateRequest().perform(duration: 0.5)
if #available(iOS 15, *), let id = consolePiPPanner_frameRateRequestID {
consolePiPPanner_frameRateRequestID = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
FrameRateRequest.shared.deactivate(id: id)
}
}
// After the PiP is thrown, determine the best corner and re-target it there.
let decelerationRate = UIScrollView.DecelerationRate.normal.rawValue
@@ -919,8 +1097,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
positionAnimator.startAnimation()
UserDefaults.standard.set(nearestTargetPosition.x, forKey: "LocalConsole_X")
UserDefaults.standard.set(nearestTargetPosition.y, forKey: "LocalConsole_Y")
UserDefaults.standard.set(nearestTargetPosition.x, forKey: "LocalConsole.X")
UserDefaults.standard.set(nearestTargetPosition.y, forKey: "LocalConsole.Y")
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.reassessGrabberMode()
@@ -939,29 +1117,37 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
// Animate touch down.
func consolePiPTouchDown() {
guard !grabberMode else { return }
UIViewPropertyAnimator(duration: 1.25, dampingRatio: 0.5) { [self] in
consoleView.transform = .init(scaleX: 0.95, y: 0.95)
}.startAnimation()
}
var consolePiPTouchDownAnimator: UIViewPropertyAnimator?
// Animate touch up.
func consolePiPTouchUp() {
UIViewPropertyAnimator(duration: 0.8, dampingRatio: 0.4) { [self] in
consoleView.transform = .init(scaleX: 1, y: 1)
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
if !grabberMode {
consoleTextView.alpha = 1
if !ResizeController.shared.isActive {
menuButton.alpha = 1
var isPressed: Bool = false {
didSet {
guard oldValue != isPressed else { return }
if isPressed {
guard !grabberMode else { return }
consolePiPTouchDownAnimator = UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 0.96, y: 0.96)
}
consolePiPTouchDownAnimator?.startAnimation(afterDelay: 0.1)
} else {
consolePiPTouchDownAnimator?.stopAnimation(true)
consolePiPPopAnimator?.stopAnimation(true)
UIViewPropertyAnimator(duration: scrollLocked ? 0.4 : 0.7, dampingRatio: scrollLocked ? 1 : 0.45) { [self] in
consoleView.transform = .identity
}.startAnimation()
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
if !grabberMode {
consoleTextView.alpha = 1
if !ResizeController.shared.isActive {
menuButton.alpha = 1
}
}
}.startAnimation()
}
}.startAnimation()
}
}
// Simulataneously listen to all gesture recognizers.
@@ -972,11 +1158,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
@objc func consolePiPTapStartEnd(recognizer: UITapStartEndGestureRecognizer) {
switch recognizer.state {
case .began:
consolePiPTouchDown()
isPressed = true
case .changed:
break
case .ended, .cancelled, .possible, .failed:
consolePiPTouchUp()
isPressed = false
@unknown default:
break
}
@@ -1044,7 +1230,7 @@ extension UIWindow {
/// Make sure this window does not have control over the status bar appearance.
static let swizzleStatusBarAppearanceOverride: Void = {
guard let originalMethod = class_getInstanceMethod(UIWindow.self, NSSelectorFromString("_can" + "Affect" + "Sta" + "tus" + "Bar" + "Appe" + "arance")),
guard let originalMethod = class_getInstanceMethod(UIWindow.self, NSSelectorFromString("_can" + "Affect" + "Status" + "Bar" + "Appearance")),
let swizzledMethod = class_getInstanceMethod(UIWindow.self, #selector(swizzled_statusBarAppearance))
else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
@@ -1055,9 +1241,36 @@ extension UIWindow {
}
}
class SwizzleTool: NSObject {
/// Ensure context menus always show in a non reversed order.
func swizzleContextMenuReverseOrder() {
guard let originalMethod = class_getInstanceMethod(NSClassFromString("_" + "UI" + "Context" + "Menu" + "List" + "View").self, NSSelectorFromString("reverses" + "Action" + "Order")),
let swizzledMethod = class_getInstanceMethod(SwizzleTool.self, #selector(swizzled_reverses_Action_Order))
else { Swift.print("Swizzle Error Occurred"); return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@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
}
if let orig = self.value(forKey: "_" + "reverses" + "Action" + "Order") as? Bool {
return orig
}
return false
}
}
class LumaView: UIView {
lazy var visualEffectView: UIView = {
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Frameworks/Material" + "Kit." + "framework")!.load()
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Framework" + "s/Material" + "Kit." + "framework")!.load()
let Pill = NSClassFromString("MT" + "Luma" + "Dodge" + "Pill" + "View") as! UIView.Type
@@ -1134,7 +1347,7 @@ class InvertedTextView: UITextView {
var pendingOffsetChange = false
// Thanks to WWDC21 Lab!
// Thanks to WWDC21 UIKit Lab!
override func layoutSubviews() {
super.layoutSubviews()
@@ -1166,6 +1379,14 @@ extension UIDevice {
}
}
extension String {
func stringAfterFirstOccurenceOf(delimiter: String) -> String? {
guard let upperIndex = (self.range(of: delimiter)?.upperBound) else { return nil }
let trailingString: String = .init(self.suffix(from: upperIndex))
return trailingString
}
}
extension TimeInterval {
var formattedString: String? {
let formatter = DateComponentsFormatter()
@@ -1181,7 +1402,6 @@ fileprivate func _debugPrint(_ items: Any) {
// Support for auto-rotate.
class ConsoleViewController: UIViewController {
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
// Cancel the panner console is being panned to allow for location manipulation.
@@ -1249,40 +1469,77 @@ An object that allows you to manually request an increased display refresh rate
```
// Example
let request = FrameRateRequest(preferredFrameRate: 120,
duration: 0.4)
FrameRateRequest.shared.perform(duration: 0.5)
request.perform()
```
*/
class FrameRateRequest {
@available(iOS 15, *)
final class FrameRateRequest {
static let shared = FrameRateRequest()
lazy private var displayLink = CADisplayLink(target: self, selector: #selector(dummyFunction))
var isActive: Bool = false {
private var requestIdentifiers: [UUID] = [] {
didSet {
guard #available(iOS 15, *) else { return }
guard isActive != oldValue else { return }
isActive = requestIdentifiers.count > 0
}
}
private var isActive: Bool = false {
didSet {
guard isActive != oldValue, UIScreen.main.maximumFramesPerSecond > 60 else { return }
if isActive {
displayLink.add(to: .current, forMode: .common)
} else {
displayLink.remove(from: .current, forMode: .common)
displayLink.invalidate()
}
}
}
/// Prepares your frame rate request parameters.
init(preferredFrameRate: Float = Float(UIScreen.main.maximumFramesPerSecond)) {
if #available(iOS 15, *) {
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 30, maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: preferredFrameRate)
}
private init() {
guard UIScreen.main.maximumFramesPerSecond > 60 else { return }
displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 90, maximum: Float(UIScreen.main.maximumFramesPerSecond), preferred: Float(UIScreen.main.maximumFramesPerSecond))
// Ensure the DisplayLink stops when the app enters the background, or else the system will shut high frame rate capabilities until the app is suspended and relaunched.
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground),
name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground),
name: UIApplication.didEnterBackgroundNotification, object: nil)
}
/// Perform frame rate request.
func perform(duration: Double) {
isActive = true
public func perform(duration: Double) {
guard UIScreen.main.maximumFramesPerSecond > 60 else { return }
let id = UUID()
requestIdentifiers.append(id)
DispatchQueue.main.asyncAfter(deadline: .now() + duration) { [self] in
isActive = false
requestIdentifiers = requestIdentifiers.filter { $0 != id }
}
}
public func activate(id: UUID) {
requestIdentifiers.append(id)
}
public func deactivate(id: UUID) {
requestIdentifiers = requestIdentifiers.filter { $0 != id }
}
@objc private func willEnterForeground() {
if isActive {
displayLink.add(to: .current, forMode: .common)
}
}
@objc private func didEnterBackground() {
if isActive {
displayLink.invalidate()
}
}
+26 -11
View File
@@ -119,7 +119,9 @@ class ResizeController {
// Ensure initial autolayout is performed unanimated.
LCManager.shared.consoleWindow?.layoutIfNeeded()
FrameRateRequest().perform(duration: 1.5)
if #available(iOS 15, *) {
FrameRateRequest.shared.perform(duration: 1.5)
}
if isActive {
@@ -207,7 +209,7 @@ class ResizeController {
static let kMinConsoleHeight: CGFloat = 108
static let kMaxConsoleHeight: CGFloat = 346
let verticalPanner_frameRateRequest = FrameRateRequest()
var verticalPanner_frameRateRequestID: UUID?
@objc func verticalPanner(recognizer: UIPanGestureRecognizer) {
@@ -218,7 +220,10 @@ class ResizeController {
switch recognizer.state {
case .began:
verticalPanner_frameRateRequest.isActive = true
if #available(iOS 15, *) {
verticalPanner_frameRateRequestID = UUID()
FrameRateRequest.shared.activate(id: verticalPanner_frameRateRequestID!)
}
initialHeight = LCManager.shared.consoleSize.height
@@ -251,9 +256,13 @@ class ResizeController {
LCManager.shared.consoleView.center.y = consoleCenterPoint.y
case .ended, .cancelled:
verticalPanner_frameRateRequest.isActive = false
FrameRateRequest().perform(duration: 0.4)
if #available(iOS 15, *), let id = verticalPanner_frameRateRequestID {
verticalPanner_frameRateRequestID = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
FrameRateRequest.shared.deactivate(id: id)
}
}
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7) {
if LCManager.shared.consoleSize.height > maxHeight {
@@ -284,7 +293,7 @@ class ResizeController {
static let kMinConsoleWidth: CGFloat = 112
static let kMaxConsoleWidth: CGFloat = [UIScreen.portraitSize.width, UIScreen.portraitSize.height].min()! - 56
let horizontalPanner_frameRateRequest = FrameRateRequest()
var horizontalPanner_frameRateRequestID: UUID?
@objc func horizontalPanner(recognizer: UIPanGestureRecognizer) {
@@ -295,7 +304,10 @@ class ResizeController {
switch recognizer.state {
case .began:
horizontalPanner_frameRateRequest.isActive = true
if #available(iOS 15, *) {
horizontalPanner_frameRateRequestID = UUID()
FrameRateRequest.shared.activate(id: horizontalPanner_frameRateRequestID!)
}
initialWidth = LCManager.shared.consoleSize.width
@@ -328,9 +340,12 @@ class ResizeController {
case .ended, .cancelled:
horizontalPanner_frameRateRequest.isActive = false
FrameRateRequest().perform(duration: 0.4)
if #available(iOS 15, *), let id = horizontalPanner_frameRateRequestID {
horizontalPanner_frameRateRequestID = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
FrameRateRequest.shared.deactivate(id: id)
}
}
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7) {
if LCManager.shared.consoleSize.width > maxWidth {