Compare commits

..

21 Commits

Author SHA1 Message Date
Duraid Abdul eeece2fda8 Update LCManager.swift
Fix for touch passthrough.
2021-12-26 00:39:38 -08:00
Duraid Abdul 01ee69b8c5 iPad Support, Landscape Support on iPhone
Full support for iPad screen sizes and screen rotation on all devices.
2021-12-26 00:27:32 -08:00
Duraid Abdul 72d9b1fbd5 Update LCManager.swift 2021-11-20 01:06:15 -07:00
Duraid Abdul b435de87a2 Add FrameRateRequest 2021-11-11 20:28:16 -08:00
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
3 changed files with 458 additions and 177 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
+369 -138
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,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
let defaultConsoleSize = CGSize(width: 228, height: 142)
let defaultConsoleSize = CGSize(width: 240, height: 148)
lazy var borderView = UIView()
@@ -62,10 +60,11 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lumaView.translatesAutoresizingMaskIntoConstraints = false
lumaWidthAnchor = lumaView.widthAnchor.constraint(equalTo: consoleView.widthAnchor)
lumaHeightAnchor = lumaView.heightAnchor.constraint(equalToConstant: consoleView.frame.size.height)
NSLayoutConstraint.activate([
lumaView.widthAnchor.constraint(equalTo: consoleView.widthAnchor),
lumaWidthAnchor,
lumaHeightAnchor,
lumaView.centerXAnchor.constraint(equalTo: consoleView.centerXAnchor),
lumaView.centerYAnchor.constraint(equalTo: consoleView.centerYAnchor)
@@ -74,6 +73,35 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
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 {
didSet {
@@ -81,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
@@ -111,12 +139,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
/// Strong reference keeps the window alive.
var consoleWindow: ConsoleWindow?
// The console needs a parent view controller in order to display context menus.
lazy var viewController = UIViewController()
lazy var consoleView = viewController.view!
/// Enables rotation.
lazy var viewController = ConsoleViewController()
/// Note: The console always needs a parent view controller in order to display context menus. In this case, the parent controller will be the viewController.
lazy var consoleView = UIView()
/// Text view that displays printed items.
lazy var consoleTextView = InvertedTextView()
lazy var consoleTextView = InvertedTextView()
/// Button that reveals menu.
lazy var menuButton = UIButton()
@@ -133,22 +163,38 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
/// Gesture endpoints. Each point represents a corner of the screen. TODO: Handle screen rotation.
var possibleEndpoints: [CGPoint] {
if consoleSize.width < UIScreen.portraitSize.width - 112 {
let screenSize = viewController.view.frame.size
// Must check for portrait mode manually here. UIDevice was reporting orientation incorrectly before.
let isPortraitNotchedPhone = UIDevice.current.hasNotch && viewController.view.frame.size.width < viewController.view.frame.size.height
// Fix incorrect reported orientation on phone.
let isLandscapePhone = (UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight) && UIDevice.current.userInterfaceIdiom == .phone
let isLandscapeLeftNotchedPhone = UIDevice.current.orientation == .landscapeLeft
&& UIDevice.current.userInterfaceIdiom == .phone
&& UIDevice.current.hasNotch
let isLandscapeRightNotchedPhone = UIDevice.current.orientation == .landscapeRight
&& UIDevice.current.userInterfaceIdiom == .phone
&& UIDevice.current.hasNotch
if consoleSize.width < screenSize.width - 112 {
// Four endpoints, one for each corner.
var endpoints = [
// Top endpoints.
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 + (isLandscapeLeftNotchedPhone ? 40 : isLandscapePhone ? 12 : 0),
y: (isPortraitNotchedPhone ? 38 : isLandscapePhone ? 0 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: screenSize.width - consoleSize.width / 2 - 12 - (isLandscapeRightNotchedPhone ? 40 : isLandscapePhone ? 12 : 0),
y: (isPortraitNotchedPhone ? 38 : isLandscapePhone ? 0 : 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)]
CGPoint(x: consoleSize.width / 2 + 12 + (isLandscapeLeftNotchedPhone ? 40 : isLandscapePhone ? 12 : 0),
y: screenSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12),
CGPoint(x: screenSize.width - consoleSize.width / 2 - 12 - (isLandscapeRightNotchedPhone ? 40 : isLandscapePhone ? 12 : 02),
y: screenSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
if consoleView.frame.minX <= 0 {
@@ -156,25 +202,29 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
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 + 10,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: -consoleSize.width / 2 + 10,
y: endpoints[1].y))
if !isLandscapeLeftNotchedPhone {
if consoleView.center.y < (screenSize.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 {
} else if consoleView.frame.maxX >= screenSize.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 - 10,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: UIScreen.portraitSize.width + consoleSize.width / 2 - 10,
y: endpoints[1].y))
if !isLandscapeRightNotchedPhone {
if consoleView.center.y < (screenSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: screenSize.width + consoleSize.width / 2 - 28,
y: endpoints[0].y))
} else {
endpoints.append(CGPoint(x: screenSize.width + consoleSize.width / 2 - 28,
y: endpoints[1].y))
}
}
}
@@ -183,29 +233,29 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
} else {
// 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),
CGPoint(x: UIScreen.portraitSize.width / 2,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
var endpoints = [CGPoint(x: screenSize.width / 2,
y: (UIScreen.hasRoundedCorners ? 38 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: screenSize.width / 2,
y: screenSize.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 + 10,
if consoleView.center.y < (screenSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
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 {
} else if consoleView.frame.maxX >= screenSize.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,
if consoleView.center.y < (screenSize.height - (temporaryKeyboardHeightValueTracker ?? 0)) / 2 {
endpoints.append(CGPoint(x: screenSize.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: screenSize.width + consoleSize.width / 2 - 28,
y: endpoints[1].y))
}
}
@@ -219,22 +269,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.center = possibleEndpoints.first!
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
@@ -243,10 +292,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
@@ -270,7 +319,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,
@@ -291,7 +340,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)
@@ -301,6 +350,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)
}
@@ -309,6 +360,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 > viewController.view.frame.size.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
@@ -324,14 +390,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleWindow?.frame = UIScreen.main.bounds
consoleWindow?.windowLevel = UIWindow.Level.statusBar
consoleWindow?.isHidden = false
consoleWindow?.addSubview(consoleView)
viewController.view = PassthroughView()
consoleWindow?.rootViewController = viewController
viewController.view.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 {
@@ -352,6 +423,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 {
@@ -361,9 +440,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)
@@ -402,22 +483,21 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
if grabberMode {
if oldValue == false {
lumaView.layer.cornerRadius = consoleView.layer.cornerRadius
lumaHeightAnchor.constant = consoleView.frame.size.height
consoleView.layoutIfNeeded()
}
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
borderView.alpha = 0
}.startAnimation()
lumaWidthAnchor.constant = -34
lumaHeightAnchor.constant = 96
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
lumaView.layer.cornerRadius = 8
@@ -425,9 +505,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.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
@@ -444,6 +527,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.startAnimation()
consoleTextView.isUserInteractionEnabled = true
unhideButton.isHidden = true
}
}
}
@@ -480,8 +564,6 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
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()
@@ -606,10 +688,6 @@ 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 {
@@ -645,22 +723,30 @@ 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()
}
})
// If device is phone in landscape, disable resize controller.
if UIDevice.current.userInterfaceIdiom == .phone && viewController.view.frame.width > viewController.view.frame.height {
resize.attributes = .disabled
if #available(iOS 15, *) {
resize.subtitle = "Portrait Orientation Only"
}
}
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])
@@ -671,14 +757,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 = {
@@ -703,28 +789,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])])
@@ -758,10 +851,10 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}.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
@@ -774,9 +867,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
}
@@ -788,21 +885,18 @@ 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 {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.grabberMode = false
}
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
self.grabberMode = true
}
}
reassessGrabberMode()
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
@@ -818,15 +912,18 @@ 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.reassessGrabberMode()
self.scrollLocked = !self.grabberMode
}
@@ -834,6 +931,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
func reassessGrabberMode() {
if consoleView.frame.maxX > 30 && consoleView.frame.minX < viewController.view.frame.size.width - 30 {
grabberMode = false
} else {
grabberMode = true
}
}
// Animate touch down.
func consolePiPTouchDown() {
guard !grabberMode else { return }
@@ -852,7 +957,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()
}
@@ -866,15 +973,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
@@ -894,6 +995,18 @@ class ConsoleWindow: UIWindow {
}
}
// Custom view for the console to appear above other windows while passing touches down.
class PassthroughView: UIView {
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if let hitView = super.hitTest(point, with: event) {
return hitView.isKind(of: PassthroughView.self) ? nil : hitView
}
return super.hitTest(point, with: event)
}
}
import UIKit.UIGestureRecognizerSubclass
@@ -917,7 +1030,7 @@ extension UIView {
let swizzledMethod = class_getInstanceMethod(UIView.self, #selector(swizzled_layoutSubviews)) else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@objc func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
@@ -942,8 +1055,6 @@ extension UIWindow {
}
}
//#endif
class LumaView: UIView {
lazy var visualEffectView: UIView = {
Bundle(path: "/Sys" + "tem/Lib" + "rary/Private" + "Frameworks/Material" + "Kit." + "framework")!.load()
@@ -1007,6 +1118,9 @@ class LumaView: UIView {
let _ = visualEffectView
let _ = foregroundView
visualEffectView.isUserInteractionEnabled = false
foregroundView.isUserInteractionEnabled = false
layer.cornerCurve = .continuous
clipsToBounds = true
}
@@ -1023,7 +1137,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 {
@@ -1046,6 +1160,12 @@ class InvertedTextView: UITextView {
}
}
extension UIDevice {
var hasNotch: Bool {
return UIApplication.shared.windows[0].safeAreaInsets.bottom > 0
}
}
extension TimeInterval {
var formattedString: String? {
let formatter = DateComponentsFormatter()
@@ -1057,3 +1177,114 @@ extension TimeInterval {
fileprivate func _debugPrint(_ items: Any) {
print(items)
}
// 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.
[LCManager.shared.panRecognizer, LCManager.shared.longPressRecognizer].forEach {
$0.isEnabled.toggle(); $0.isEnabled.toggle()
}
if UIDevice.current.userInterfaceIdiom != .pad && ResizeController.shared.isActive {
ResizeController.shared.isActive = false
ResizeController.shared.platterView.dismiss()
}
if UIDevice.current.userInterfaceIdiom == .pad && ResizeController.shared.isActive {
DispatchQueue.main.async {
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
LCManager.shared.consoleView.center = ResizeController.shared.consoleCenterPoint
}.startAnimation(afterDelay: 0.05)
}
} else {
let consoleView = LCManager.shared.consoleView
let oldSize = LCManager.shared.viewController.view.frame.size
let targetLocationEstimate: CGPoint = {
var xPosition = consoleView.center.x
var yPosition = consoleView.center.y
if consoleView.center.x > oldSize.width / 2 {
xPosition += size.width - oldSize.width
}
if consoleView.center.y > oldSize.height / 2 {
yPosition += size.height - oldSize.height
}
return CGPoint(x: xPosition, y: yPosition)
}()
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
consoleView.center = targetLocationEstimate
}.startAnimation(afterDelay: 0.05)
DispatchQueue.main.async {
// Update portrait orientation menu option for resize controller.
LCManager.shared.menuButton.menu = LCManager.shared.makeMenu()
// Reassess center of console based on target location estimate.
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
consoleView.center = nearestTargetTo(consoleView.center, possibleTargets: LCManager.shared.possibleEndpoints)
}.startAnimation(afterDelay: 0.05)
LCManager.shared.reassessGrabberMode()
}
}
}
}
// 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() {}
}
+87 -32
View File
@@ -14,9 +14,13 @@ class ResizeController {
lazy var platterView = PlatterView(frame: .zero)
lazy var consoleCenterPoint = CGPoint(x: (UIScreen.main.nativeBounds.width / 2).rounded() / UIScreen.main.scale,
y: (UIScreen.main.nativeBounds.height / 2).rounded() / UIScreen.main.scale
+ (UIScreen.hasRoundedCorners ? 0 : 24))
var consoleCenterPoint: CGPoint {
let containerViewSize = LCManager.shared.viewController.view.frame.size
return CGPoint(x: (containerViewSize.width * UIScreen.main.scale / 2).rounded() / UIScreen.main.scale,
y: (containerViewSize.height * UIScreen.main.scale / 2).rounded() / UIScreen.main.scale
+ (UIScreen.hasRoundedCorners ? 0 : 24))
}
lazy var consoleOutlineView: UIView = {
@@ -115,6 +119,8 @@ class ResizeController {
// Ensure initial autolayout is performed unanimated.
LCManager.shared.consoleWindow?.layoutIfNeeded()
FrameRateRequest().perform(duration: 1.5)
if isActive {
UIViewPropertyAnimator(duration: 0.75, dampingRatio: 1) {
@@ -170,7 +176,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()
@@ -201,6 +207,8 @@ class ResizeController {
static let kMinConsoleHeight: CGFloat = 108
static let kMaxConsoleHeight: CGFloat = 346
let verticalPanner_frameRateRequest = FrameRateRequest()
@objc func verticalPanner(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: bottomGrabber.superview)
@@ -210,6 +218,8 @@ class ResizeController {
switch recognizer.state {
case .began:
verticalPanner_frameRateRequest.isActive = true
initialHeight = LCManager.shared.consoleSize.height
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
@@ -241,6 +251,10 @@ class ResizeController {
LCManager.shared.consoleView.center.y = consoleCenterPoint.y
case .ended, .cancelled:
verticalPanner_frameRateRequest.isActive = false
FrameRateRequest().perform(duration: 0.4)
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7) {
if LCManager.shared.consoleSize.height > maxHeight {
LCManager.shared.consoleSize.height = maxHeight
@@ -268,7 +282,9 @@ class ResizeController {
var initialWidth = CGFloat.zero
static let kMinConsoleWidth: CGFloat = 112
static let kMaxConsoleWidth: CGFloat = UIScreen.portraitSize.width - 56
static let kMaxConsoleWidth: CGFloat = [UIScreen.portraitSize.width, UIScreen.portraitSize.height].min()! - 56
let horizontalPanner_frameRateRequest = FrameRateRequest()
@objc func horizontalPanner(recognizer: UIPanGestureRecognizer) {
@@ -279,6 +295,8 @@ class ResizeController {
switch recognizer.state {
case .began:
horizontalPanner_frameRateRequest.isActive = true
initialWidth = LCManager.shared.consoleSize.width
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
@@ -310,6 +328,10 @@ class ResizeController {
case .ended, .cancelled:
horizontalPanner_frameRateRequest.isActive = false
FrameRateRequest().perform(duration: 0.4)
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 0.7) {
if LCManager.shared.consoleSize.width > maxWidth {
LCManager.shared.consoleSize.width = maxWidth
@@ -339,12 +361,6 @@ class PlatterView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.frame.size = UIScreen.portraitSize
// Make sure bottom doesn't show on upwards pan.
self.frame.size.height += 50
self.frame.origin = possibleEndpoints[1]
autoresizingMask = [.flexibleWidth, .flexibleHeight]
layer.shadowRadius = 10
layer.shadowOpacity = 0.125
layer.shadowOffset = CGSize(width: 0, height: 0)
@@ -361,11 +377,12 @@ class PlatterView: UIView {
blurView.clipsToBounds = true
blurView.frame = bounds
blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(blurView)
LCManager.shared.consoleWindow?.addSubview(self)
LCManager.shared.consoleWindow?.sendSubviewToBack(self)
LCManager.shared.viewController.view.addSubview(self)
LCManager.shared.viewController.view.sendSubviewToBack(self)
_ = backgroundButton
@@ -376,7 +393,7 @@ class PlatterView: UIView {
let grabber = UIView()
grabber.frame.size = CGSize(width: 36, height: 5)
grabber.frame.origin.y = 10
grabber.center.x = bounds.width / 2
grabber.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin]
grabber.backgroundColor = .label
grabber.alpha = 0.1
grabber.layer.cornerRadius = 2.5
@@ -387,9 +404,8 @@ class PlatterView: UIView {
titleLabel.text = "Resize Console"
titleLabel.font = .systemFont(ofSize: 30, weight: .bold)
titleLabel.sizeToFit()
titleLabel.center.x = bounds.width / 2
titleLabel.frame.origin.y = 28
titleLabel.roundOriginToPixel()
titleLabel.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin]
addSubview(titleLabel)
let subtitleLabel = UILabel()
@@ -397,20 +413,36 @@ class PlatterView: UIView {
subtitleLabel.font = .systemFont(ofSize: 17, weight: .medium)
subtitleLabel.sizeToFit()
subtitleLabel.alpha = 0.5
subtitleLabel.center.x = bounds.width / 2
subtitleLabel.frame.origin.y = titleLabel.frame.maxY + 8
subtitleLabel.roundOriginToPixel()
subtitleLabel.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin]
addSubview(subtitleLabel)
addSubview(resetButton)
resetButton.center = CGPoint(x: UIScreen.portraitSize.width / 2 - 74,
y: UIScreen.portraitSize.height - possibleEndpoints[0].y * 2)
resetButton.roundOriginToPixel()
let buttonContainerView = UIView()
buttonContainerView.addSubview(resetButton)
buttonContainerView.addSubview(doneButton)
addSubview(buttonContainerView)
addSubview(doneButton)
doneButton.center = CGPoint(x: UIScreen.portraitSize.width / 2 + 74,
y: UIScreen.portraitSize.height - possibleEndpoints[0].y * 2)
doneButton.roundOriginToPixel()
buttonContainerView.translatesAutoresizingMaskIntoConstraints = false
resetButton.translatesAutoresizingMaskIntoConstraints = false
doneButton.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
buttonContainerView.widthAnchor.constraint(equalToConstant: 264),
buttonContainerView.heightAnchor.constraint(equalToConstant: 52),
buttonContainerView.centerXAnchor.constraint(equalTo: centerXAnchor),
buttonContainerView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -possibleEndpoints[0].y * 2),
resetButton.widthAnchor.constraint(equalToConstant: 116),
resetButton.heightAnchor.constraint(equalToConstant: 52),
resetButton.leadingAnchor.constraint(equalTo: buttonContainerView.leadingAnchor),
resetButton.topAnchor.constraint(equalTo: buttonContainerView.topAnchor),
doneButton.widthAnchor.constraint(equalToConstant: 116),
doneButton.heightAnchor.constraint(equalToConstant: 52),
doneButton.trailingAnchor.constraint(equalTo: buttonContainerView.trailingAnchor),
doneButton.topAnchor.constraint(equalTo: buttonContainerView.topAnchor)
])
}
lazy var backgroundButton: UIButton = {
@@ -430,7 +462,6 @@ class PlatterView: UIView {
button.setTitle("Done", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 17, weight: .medium)
button.frame.size = CGSize(width: 116, height: 52)
button.layer.cornerRadius = 20
button.layer.cornerCurve = .continuous
@@ -465,7 +496,6 @@ class PlatterView: UIView {
button.setTitle("Reset", for: .normal)
button.setTitleColor(.label, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 17, weight: .medium)
button.frame.size = CGSize(width: 116, height: 52)
button.layer.cornerRadius = 20
button.layer.cornerCurve = .continuous
@@ -478,6 +508,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()
@@ -497,18 +528,35 @@ class PlatterView: UIView {
return button
}()
func configureFrame() {
self.frame.size = LCManager.shared.viewController.view.frame.size
// Make sure bottom doesn't show on upwards pan.
self.frame.size.height += 50
self.frame.origin = possibleEndpoints[1]
autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
func reveal() {
configureFrame()
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
self.frame.origin = self.possibleEndpoints[0]
}.startAnimation()
backgroundButton.isHidden = false
isHidden = false
}
func dismiss() {
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
let animator = UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
self.frame.origin = self.possibleEndpoints[1]
}.startAnimation()
}
animator.addCompletion { _ in
self.isHidden = true
}
animator.startAnimation()
backgroundButton.isHidden = true
}
@@ -529,7 +577,9 @@ class PlatterView: UIView {
fatalError("init(coder:) has not been implemented")
}
lazy var possibleEndpoints = [CGPoint(x: 0, y: (UIScreen.hasRoundedCorners ? 44 : -8) + 63), CGPoint(x: 0, y: UIScreen.portraitSize.height + 5)]
var possibleEndpoints: [CGPoint] { return [CGPoint(x: 0, y: (UIScreen.hasRoundedCorners ? 44 : -8) + 63),
CGPoint(x: 0, y: LCManager.shared.viewController.view.frame.size.height + 5)]
}
var initialPlatterOriginY = CGFloat.zero
@@ -611,15 +661,20 @@ class PlatterView: UIView {
$0.transform = .identity
}
}
positionAnimator.startAnimation()
if nearestTargetPosition == possibleEndpoints[1] {
ResizeController.shared.isActive = false
backgroundButton.isHidden = true
positionAnimator.addCompletion { _ in
self.isHidden = true
}
} else {
ResizeController.shared.isActive = true
}
positionAnimator.startAnimation()
default: break
}
}