|
|
|
@@ -15,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 }
|
|
|
|
@@ -44,7 +44,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let defaultConsoleSize = CGSize(width: 228, height: 142)
|
|
|
|
|
let defaultConsoleSize = CGSize(width: 240, height: 148)
|
|
|
|
|
|
|
|
|
|
lazy var borderView = UIView()
|
|
|
|
|
|
|
|
|
@@ -73,7 +73,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
return lumaView
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
lazy var hideButton: UIButton = {
|
|
|
|
|
lazy var unhideButton: UIButton = {
|
|
|
|
|
let button = UIButton()
|
|
|
|
|
|
|
|
|
|
button.addAction(UIAction(handler: { [self] _ in
|
|
|
|
@@ -81,6 +81,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
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)
|
|
|
|
@@ -106,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
|
|
|
|
@@ -165,9 +168,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
// Top endpoints.
|
|
|
|
|
CGPoint(x: consoleSize.width / 2 + 12,
|
|
|
|
|
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
|
|
|
|
|
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
|
|
|
|
|
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
|
|
|
|
|
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
|
|
|
|
|
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
|
|
|
|
|
|
|
|
|
|
// Bottom endpoints.
|
|
|
|
|
CGPoint(x: consoleSize.width / 2 + 12,
|
|
|
|
@@ -209,7 +212,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
// Two endpoints, one for the top, one for the bottom..
|
|
|
|
|
var endpoints = [CGPoint(x: UIScreen.portraitSize.width / 2,
|
|
|
|
|
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
|
|
|
|
|
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)]
|
|
|
|
|
|
|
|
|
@@ -217,20 +220,20 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
// Left edge hiding endpoints.
|
|
|
|
|
if consoleView.center.y < (UIScreen.portraitSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
|
|
|
|
|
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 10,
|
|
|
|
|
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 28,
|
|
|
|
|
y: endpoints[0].y))
|
|
|
|
|
} else {
|
|
|
|
|
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 10,
|
|
|
|
|
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 - 10,
|
|
|
|
|
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 - 10,
|
|
|
|
|
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 28,
|
|
|
|
|
y: endpoints[1].y))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -244,21 +247,21 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
consoleView.layer.shadowRadius = 16
|
|
|
|
|
consoleView.layer.shadowOpacity = 0.5
|
|
|
|
|
consoleView.layer.shadowOffset = CGSize(width: 0, height: 2)
|
|
|
|
|
consoleView.alpha = 0
|
|
|
|
|
|
|
|
|
|
consoleView.layer.cornerRadius = 22
|
|
|
|
|
consoleView.layer.cornerRadius = 24
|
|
|
|
|
consoleView.layer.cornerCurve = .continuous
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
@@ -267,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
|
|
|
|
@@ -294,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,
|
|
|
|
@@ -315,7 +318,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
menuButton.addSubview(circle)
|
|
|
|
|
|
|
|
|
|
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis",
|
|
|
|
|
withConfiguration: UIImage.SymbolConfiguration(pointSize: 17, weight: .medium)))
|
|
|
|
|
withConfiguration: UIImage.SymbolConfiguration(pointSize: 18, weight: .medium)))
|
|
|
|
|
ellipsisImage.frame.size = circle.bounds.size
|
|
|
|
|
ellipsisImage.contentMode = .center
|
|
|
|
|
circle.addSubview(ellipsisImage)
|
|
|
|
@@ -325,7 +328,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
menuButton.showsMenuAsPrimaryAction = true
|
|
|
|
|
consoleView.addSubview(menuButton)
|
|
|
|
|
|
|
|
|
|
let _ = hideButton
|
|
|
|
|
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)
|
|
|
|
@@ -337,11 +340,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
|
|
|
|
|
// Update console cached based on last-cached origin.
|
|
|
|
|
func updateConsoleOrigin() {
|
|
|
|
|
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)
|
|
|
|
|
snapToCachedEndpoint()
|
|
|
|
|
|
|
|
|
|
if consoleView.center.x < 0 || consoleView.center.x > UIScreen.portraitSize.width {
|
|
|
|
|
grabberMode = true
|
|
|
|
@@ -397,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 {
|
|
|
|
@@ -406,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)
|
|
|
|
@@ -469,7 +478,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}.startAnimation(afterDelay: 0.06)
|
|
|
|
|
|
|
|
|
|
consoleTextView.isUserInteractionEnabled = false
|
|
|
|
|
hideButton.isHidden = false
|
|
|
|
|
unhideButton.isHidden = false
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
@@ -491,7 +500,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}.startAnimation()
|
|
|
|
|
|
|
|
|
|
consoleTextView.isUserInteractionEnabled = true
|
|
|
|
|
hideButton.isHidden = true
|
|
|
|
|
unhideButton.isHidden = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -689,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])
|
|
|
|
|
|
|
|
|
@@ -715,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 = {
|
|
|
|
@@ -747,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])])
|
|
|
|
@@ -818,9 +834,13 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let consolePiPPanner_frameRateRequest = FrameRateRequest()
|
|
|
|
|
|
|
|
|
|
@objc func consolePiPPanner(recognizer: UIPanGestureRecognizer) {
|
|
|
|
|
|
|
|
|
|
if recognizer.state == .began {
|
|
|
|
|
consolePiPPanner_frameRateRequest.isActive = true
|
|
|
|
|
|
|
|
|
|
initialViewLocation = consoleView.center
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -832,17 +852,22 @@ 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 {
|
|
|
|
|
self.grabberMode = false
|
|
|
|
|
grabberMode = false
|
|
|
|
|
} else {
|
|
|
|
|
self.grabberMode = true
|
|
|
|
|
grabberMode = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case .ended, .cancelled:
|
|
|
|
|
|
|
|
|
|
consolePiPPanner_frameRateRequest.isActive = false
|
|
|
|
|
FrameRateRequest().perform(duration: 0.5)
|
|
|
|
|
|
|
|
|
|
// After the PiP is thrown, determine the best corner and re-target it there.
|
|
|
|
|
let decelerationRate = UIScrollView.DecelerationRate.normal.rawValue
|
|
|
|
|
|
|
|
|
@@ -895,7 +920,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
|
|
|
|
|
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
|
|
|
|
|
if !grabberMode {
|
|
|
|
|
consoleTextView.alpha = 1
|
|
|
|
|
menuButton.alpha = 1
|
|
|
|
|
if !ResizeController.shared.isActive {
|
|
|
|
|
menuButton.alpha = 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}.startAnimation()
|
|
|
|
|
}
|
|
|
|
@@ -954,7 +981,7 @@ extension UIView {
|
|
|
|
|
let swizzledMethod = class_getInstanceMethod(UIView.self, #selector(swizzled_layoutSubviews)) else { return }
|
|
|
|
|
method_exchangeImplementations(originalMethod, swizzledMethod)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@objc func swizzled_layoutSubviews() {
|
|
|
|
|
swizzled_layoutSubviews()
|
|
|
|
|
|
|
|
|
@@ -1061,7 +1088,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 {
|
|
|
|
@@ -1095,3 +1122,53 @@ extension TimeInterval {
|
|
|
|
|
fileprivate func _debugPrint(_ items: Any) {
|
|
|
|
|
print(items)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MARK: Frame Rate Request
|
|
|
|
|
/**
|
|
|
|
|
An object that allows you to manually request an increased display refresh rate on ProMotion devices.
|
|
|
|
|
|
|
|
|
|
*The display refresh rate does not exceed 60 Hz when low power mode is enabled.*
|
|
|
|
|
|
|
|
|
|
**Do not set an excessive duration. Doing so will negatively impact battery life.**
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
// Example
|
|
|
|
|
let request = FrameRateRequest(preferredFrameRate: 120,
|
|
|
|
|
duration: 0.4)
|
|
|
|
|
request.perform()
|
|
|
|
|
```
|
|
|
|
|
*/
|
|
|
|
|
class FrameRateRequest {
|
|
|
|
|
|
|
|
|
|
lazy private var displayLink = CADisplayLink(target: self, selector: #selector(dummyFunction))
|
|
|
|
|
|
|
|
|
|
var isActive: Bool = false {
|
|
|
|
|
didSet {
|
|
|
|
|
guard #available(iOS 15, *) else { return }
|
|
|
|
|
guard isActive != oldValue else { return }
|
|
|
|
|
|
|
|
|
|
if isActive {
|
|
|
|
|
displayLink.add(to: .current, forMode: .common)
|
|
|
|
|
} else {
|
|
|
|
|
displayLink.remove(from: .current, forMode: .common)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// 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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Perform frame rate request.
|
|
|
|
|
func perform(duration: Double) {
|
|
|
|
|
isActive = true
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + duration) { [self] in
|
|
|
|
|
isActive = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@objc private func dummyFunction() {}
|
|
|
|
|
}
|
|
|
|
|