Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80e0aa6168 | |||
| 1dcb6e3a57 | |||
| bfc3bc4fd2 | |||
| 74556927be | |||
| fee9e30df9 | |||
| 5e2b5feca4 | |||
| 3ffdde8904 | |||
| 63ebc8ed31 | |||
| 64e18b18fc | |||
| 9dcfa5accd | |||
| adbb2d763f | |||
| c08c26f4a7 | |||
| d995119198 | |||
| b4d7c06432 | |||
| a7b95a4379 | |||
| 3efe25f804 | |||
| 0a3e28b28f | |||
| 009fab95be | |||
| a1275a7f49 | |||
| e08d439d2b | |||
| da9d78f559 | |||
| 50e4ce4e03 | |||
| ae73be37b4 | |||
| eeece2fda8 | |||
| 01ee69b8c5 | |||
| 72d9b1fbd5 | |||
| b435de87a2 | |||
| 372c8ce90b | |||
| 0d36867f6b | |||
| 3fff6edff0 | |||
| 485126dcf7 | |||
| 245d69679d | |||
| 7920272bff | |||
| 6892a19b0e | |||
| d2d45f8e03 | |||
| d5a06c013e | |||
| 3c2683c6bf | |||
| fd1114802f | |||
| afe572f4e3 | |||
| ceb5ed0a0c | |||
| 9189fa4173 | |||
| 1e39b362cc | |||
| 265eaeadad | |||
| cf0d3beb76 | |||
| ec386069a2 | |||
| ab36ae5bb8 | |||
| e6846048d0 | |||
| d6d9aad082 | |||
| 6723947fe6 | |||
| 24a7a197b0 | |||
| ab79c1200f | |||
| 0ae97b8162 |
@@ -13,7 +13,7 @@ Welcome to LocalConsole! This Swift Package makes on-device debugging easy with
|
||||
|
||||
2. Paste the following into the URL field: https://github.com/duraidabdul/LocalConsole/
|
||||
|
||||
3. Once the package dependancy has been added, import LocalConsole and create an easily accessible global instance of ```Console.shared```.
|
||||
3. Once the package dependancy has been added, import LocalConsole and create an easily accessible global instance of ```LCManager.shared```.
|
||||
```swift
|
||||
import LocalConsole
|
||||
|
||||
@@ -21,13 +21,13 @@ 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
|
||||
|
||||
// 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
|
||||
|
||||
+1009
-226
File diff suppressed because it is too large
Load Diff
@@ -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.consoleViewController.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 = {
|
||||
|
||||
@@ -46,7 +50,7 @@ class ResizeController {
|
||||
|
||||
lazy var bottomGrabber: UIView = {
|
||||
let view = UIView()
|
||||
LCManager.shared.consoleWindow?.addSubview(view)
|
||||
LCManager.shared.consoleViewController.view.addSubview(view)
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
@@ -76,7 +80,7 @@ class ResizeController {
|
||||
|
||||
lazy var rightGrabber: UIView = {
|
||||
let view = UIView()
|
||||
LCManager.shared.consoleWindow?.addSubview(view)
|
||||
LCManager.shared.consoleViewController.view.addSubview(view)
|
||||
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
@@ -113,7 +117,11 @@ class ResizeController {
|
||||
_ = rightGrabber
|
||||
|
||||
// Ensure initial autolayout is performed unanimated.
|
||||
LCManager.shared.consoleWindow?.layoutIfNeeded()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
|
||||
if #available(iOS 15, *) {
|
||||
FrameRateRequest.shared.perform(duration: 1.5)
|
||||
}
|
||||
|
||||
if isActive {
|
||||
|
||||
@@ -130,17 +138,17 @@ class ResizeController {
|
||||
}
|
||||
|
||||
// Ensure background color animates in right the first time.
|
||||
LCManager.shared.consoleWindow?.backgroundColor = .clear
|
||||
LCManager.shared.consoleViewController.view.backgroundColor = .clear
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.6, dampingRatio: 1) {
|
||||
LCManager.shared.consoleView.center = self.consoleCenterPoint
|
||||
|
||||
// Update grabbers (layout constraints)
|
||||
LCManager.shared.consoleWindow?.layoutIfNeeded()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
|
||||
LCManager.shared.menuButton.alpha = 0
|
||||
|
||||
LCManager.shared.consoleWindow?.backgroundColor = UIColor(dynamicProvider: { traitCollection in
|
||||
LCManager.shared.consoleViewController.view.backgroundColor = UIColor(dynamicProvider: { traitCollection in
|
||||
UIColor(white: 0, alpha: traitCollection.userInterfaceStyle == .light ? 0.1 : 0.3)
|
||||
})
|
||||
}.startAnimation()
|
||||
@@ -170,14 +178,14 @@ 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()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
|
||||
LCManager.shared.menuButton.alpha = 1
|
||||
|
||||
LCManager.shared.consoleWindow?.backgroundColor = .clear
|
||||
LCManager.shared.consoleViewController.view.backgroundColor = .clear
|
||||
}.startAnimation()
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.2, dampingRatio: 1) { [self] in
|
||||
@@ -201,6 +209,8 @@ class ResizeController {
|
||||
static let kMinConsoleHeight: CGFloat = 108
|
||||
static let kMaxConsoleHeight: CGFloat = 346
|
||||
|
||||
var verticalPanner_frameRateRequestID: UUID?
|
||||
|
||||
@objc func verticalPanner(recognizer: UIPanGestureRecognizer) {
|
||||
|
||||
let translation = recognizer.translation(in: bottomGrabber.superview)
|
||||
@@ -210,6 +220,11 @@ class ResizeController {
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if #available(iOS 15, *) {
|
||||
verticalPanner_frameRateRequestID = UUID()
|
||||
FrameRateRequest.shared.activate(id: verticalPanner_frameRateRequestID!)
|
||||
}
|
||||
|
||||
initialHeight = LCManager.shared.consoleSize.height
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
|
||||
@@ -236,22 +251,33 @@ class ResizeController {
|
||||
}
|
||||
}()
|
||||
|
||||
LCManager.shared.lumaHeightAnchor.constant = resolvedHeight
|
||||
LCManager.shared.consoleSize.height = resolvedHeight
|
||||
LCManager.shared.consoleView.center.y = consoleCenterPoint.y
|
||||
|
||||
case .ended, .cancelled:
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
// Animate autolayout updates.
|
||||
LCManager.shared.consoleWindow?.layoutIfNeeded()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
}.startAnimation()
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
|
||||
@@ -265,7 +291,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
|
||||
|
||||
var horizontalPanner_frameRateRequestID: UUID?
|
||||
|
||||
@objc func horizontalPanner(recognizer: UIPanGestureRecognizer) {
|
||||
|
||||
@@ -276,6 +304,11 @@ class ResizeController {
|
||||
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
if #available(iOS 15, *) {
|
||||
horizontalPanner_frameRateRequestID = UUID()
|
||||
FrameRateRequest.shared.activate(id: horizontalPanner_frameRateRequestID!)
|
||||
}
|
||||
|
||||
initialWidth = LCManager.shared.consoleSize.width
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
|
||||
@@ -307,6 +340,13 @@ class ResizeController {
|
||||
|
||||
case .ended, .cancelled:
|
||||
|
||||
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 {
|
||||
LCManager.shared.consoleSize.width = maxWidth
|
||||
@@ -318,7 +358,7 @@ class ResizeController {
|
||||
LCManager.shared.consoleView.center.x = (UIScreen.main.nativeBounds.width * 1/2).rounded() / UIScreen.main.scale
|
||||
|
||||
// Animate autolayout updates.
|
||||
LCManager.shared.consoleWindow?.layoutIfNeeded()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
}.startAnimation()
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
|
||||
@@ -336,12 +376,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)
|
||||
@@ -358,11 +392,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.consoleViewController.view.addSubview(self)
|
||||
LCManager.shared.consoleViewController.view.sendSubviewToBack(self)
|
||||
|
||||
_ = backgroundButton
|
||||
|
||||
@@ -373,7 +408,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
|
||||
@@ -384,9 +419,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()
|
||||
@@ -394,20 +428,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 = {
|
||||
@@ -416,8 +466,8 @@ class PlatterView: UIView {
|
||||
self.dismiss()
|
||||
}))
|
||||
backgroundButton.frame.size = CGSize(width: self.frame.size.width, height: possibleEndpoints[0].y + 30)
|
||||
LCManager.shared.consoleWindow?.addSubview(backgroundButton)
|
||||
LCManager.shared.consoleWindow?.sendSubviewToBack(backgroundButton)
|
||||
LCManager.shared.consoleViewController.view.addSubview(backgroundButton)
|
||||
LCManager.shared.consoleViewController.view.sendSubviewToBack(backgroundButton)
|
||||
return backgroundButton
|
||||
}()
|
||||
|
||||
@@ -427,7 +477,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
|
||||
|
||||
@@ -462,7 +511,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
|
||||
|
||||
@@ -475,8 +523,9 @@ 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()
|
||||
LCManager.shared.consoleViewController.view.layoutIfNeeded()
|
||||
}.startAnimation()
|
||||
|
||||
}), for: .touchUpInside)
|
||||
@@ -494,18 +543,35 @@ class PlatterView: UIView {
|
||||
return button
|
||||
}()
|
||||
|
||||
func configureFrame() {
|
||||
self.frame.size = LCManager.shared.consoleViewController.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
|
||||
}
|
||||
@@ -526,7 +592,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.consoleViewController.view.frame.size.height + 5)]
|
||||
}
|
||||
|
||||
var initialPlatterOriginY = CGFloat.zero
|
||||
|
||||
@@ -608,15 +676,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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user