Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 4b79e2744d | |||
| 641e20bb01 | |||
| bc6c4a91ba | |||
| 82605fcfbb | |||
| 27876dfba9 | |||
| 1a2da892ac | |||
| c9bfed3373 | |||
| 3f732f5054 | |||
| 33de7eb54a | |||
| bad02ce90b | |||
| c2cdc1c822 | |||
| 61ed2d92db | |||
| a522748e3c | |||
| 58aecf3a9d | |||
| 971de252bf | |||
| ef0bd6cd8a | |||
| 5f8c210c62 | |||
| 0bbfabc568 | |||
| 157ab2153f | |||
| 51ddd6c2c2 | |||
| c5a5641906 | |||
| cbbbf2c6db | |||
| a75c5763a4 | |||
| a66afaef04 | |||
| 7ca52868ff | |||
| 417bdf2fb3 | |||
| 92299c62de | |||
| 22cd8d12ff | |||
| de62ed79af | |||
| 261ba2a83b | |||
| 697d636cbc | |||
| cdbb4fef4d | |||
| dea5e6e4e0 | |||
| b28605e52e | |||
| 35a5350f17 |
Binary file not shown.
|
Before Width: | Height: | Size: 10 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 11 MiB |
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Duraid Abdul
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -3,8 +3,8 @@
|
||||
Welcome to LocalConsole! This Swift Package makes on-device debugging easy with a convenient PiP-style console that can display items in the same way ```print()``` will in Xcode. This tool can also dynamically display view frames and restart SpringBoard right from your live app.
|
||||
|
||||
<div>
|
||||
<img src="https://github.com/duraidabdul/LocalConsole/blob/main/Additional%20Files/Demo_Pan.gif?raw=true" width="320">
|
||||
<img src="https://github.com/duraidabdul/LocalConsole/blob/main/Additional%20Files/Demo_Resize.gif?raw=true" width="320">
|
||||
<img src="https://github.com/duraidabdul/Demos/blob/main/Demo_Pan.gif?raw=true" width="320">
|
||||
<img src="https://github.com/duraidabdul/Demos/blob/main/Demo_Resize.gif?raw=true" width="320">
|
||||
</div>
|
||||
|
||||
## **Setup**
|
||||
@@ -17,38 +17,32 @@ Welcome to LocalConsole! This Swift Package makes on-device debugging easy with
|
||||
```swift
|
||||
import LocalConsole
|
||||
|
||||
let localConsoleManager = LCManager.shared
|
||||
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.
|
||||
localConsoleManager.isVisible = true
|
||||
// Activate the console view.
|
||||
consoleManager.isVisible = true
|
||||
|
||||
// Hide the console view.
|
||||
localConsoleManager.isVisible = false
|
||||
// Deactivate the console view.
|
||||
consoleManager.isVisible = false
|
||||
```
|
||||
|
||||
```swift
|
||||
// Print items to the console view.
|
||||
localConsoleManager.print("Hello, world!")
|
||||
consoleManager.print("Hello, world!")
|
||||
|
||||
// Clear console text.
|
||||
localConsoleManager.clear()
|
||||
consoleManager.clear()
|
||||
|
||||
// Copy console text.
|
||||
localConsoleManager.copy()
|
||||
consoleManager.copy()
|
||||
```
|
||||
|
||||
```swift
|
||||
// Change the console view font size.
|
||||
localConsoleManager.fontSize = 5
|
||||
consoleManager.fontSize = 5
|
||||
```
|
||||
|
||||
|
||||
## **To-Do**
|
||||
* Support for iOS 13
|
||||
* Screen edge console hiding
|
||||
* Make console view reactive to landscape/portrait switch
|
||||
|
||||
@@ -22,6 +22,7 @@ extension UIScreen {
|
||||
static var hasRoundedCorners = UIScreen.main.value(forKey: "_" + "display" + "Corner" + "Radius") as! CGFloat > 0
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
extension UIApplication {
|
||||
var statusBarHeight: CGFloat {
|
||||
if let window = UIApplication.shared.windows.first {
|
||||
|
||||
+1122
-193
File diff suppressed because it is too large
Load Diff
@@ -7,15 +7,20 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
class ResizeController {
|
||||
|
||||
public static let shared = 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 = {
|
||||
|
||||
@@ -114,8 +119,20 @@ class ResizeController {
|
||||
// Ensure initial autolayout is performed unanimated.
|
||||
LCManager.shared.consoleWindow?.layoutIfNeeded()
|
||||
|
||||
if #available(iOS 15, *) {
|
||||
FrameRateRequest.shared.perform(duration: 1.5)
|
||||
}
|
||||
|
||||
if isActive {
|
||||
|
||||
UIViewPropertyAnimator(duration: 0.75, dampingRatio: 1) {
|
||||
|
||||
let textView = LCManager.shared.consoleTextView
|
||||
|
||||
textView.contentOffset.y = textView.contentSize.height - textView.bounds.size.height
|
||||
}.startAnimation()
|
||||
|
||||
|
||||
if LCManager.shared.consoleView.traitCollection.userInterfaceStyle == .light {
|
||||
LCManager.shared.consoleView.layer.shadowOpacity = 0.25
|
||||
}
|
||||
@@ -161,7 +178,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()
|
||||
@@ -189,15 +206,25 @@ class ResizeController {
|
||||
|
||||
var initialHeight = CGFloat.zero
|
||||
|
||||
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)
|
||||
|
||||
let maxHeight: CGFloat = 346
|
||||
let minHeight: CGFloat = 108
|
||||
let minHeight = Self.kMinConsoleHeight
|
||||
let maxHeight = Self.kMaxConsoleHeight
|
||||
|
||||
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
|
||||
@@ -224,16 +251,27 @@ 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
|
||||
@@ -252,17 +290,25 @@ class ResizeController {
|
||||
|
||||
var initialWidth = CGFloat.zero
|
||||
|
||||
static let kMaxConsoleWidth: CGFloat = UIScreen.portraitSize.width - 56
|
||||
static let kMinConsoleWidth: CGFloat = 112
|
||||
static let kMaxConsoleWidth: CGFloat = [UIScreen.portraitSize.width, UIScreen.portraitSize.height].min()! - 56
|
||||
|
||||
var horizontalPanner_frameRateRequestID: UUID?
|
||||
|
||||
@objc func horizontalPanner(recognizer: UIPanGestureRecognizer) {
|
||||
|
||||
let translation = recognizer.translation(in: bottomGrabber.superview)
|
||||
|
||||
let maxWidth: CGFloat = Self.kMaxConsoleWidth
|
||||
let minWidth: CGFloat = 112
|
||||
let minWidth = Self.kMinConsoleWidth
|
||||
let maxWidth = Self.kMaxConsoleWidth
|
||||
|
||||
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
|
||||
@@ -294,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
|
||||
@@ -317,17 +370,12 @@ class ResizeController {
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOSApplicationExtension, unavailable)
|
||||
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)
|
||||
@@ -344,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.viewController.view.addSubview(self)
|
||||
LCManager.shared.viewController.view.sendSubviewToBack(self)
|
||||
|
||||
_ = backgroundButton
|
||||
|
||||
@@ -359,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
|
||||
@@ -370,29 +419,45 @@ 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()
|
||||
subtitleLabel.text = "Use the grabbers to resize the console."
|
||||
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 = {
|
||||
@@ -412,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
|
||||
|
||||
@@ -447,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
|
||||
|
||||
@@ -455,11 +518,12 @@ class PlatterView: UIView {
|
||||
|
||||
// Resolves a text view frame animation bug that occurs when *decreasing* text view width.
|
||||
if LCManager.shared.consoleSize.width > LCManager.shared.defaultConsoleSize.width {
|
||||
LCManager.shared.consoleTextView.frame.size.width = LCManager.shared.defaultConsoleSize.width
|
||||
LCManager.shared.consoleTextView.frame.size.width = LCManager.shared.defaultConsoleSize.width - 4
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -479,18 +543,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
|
||||
}
|
||||
@@ -511,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.viewController.view.frame.size.height + 5)]
|
||||
}
|
||||
|
||||
var initialPlatterOriginY = CGFloat.zero
|
||||
|
||||
@@ -593,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,20 +32,20 @@ class SystemReport {
|
||||
|
||||
// Retrieve device mobile gestalt cache.
|
||||
lazy var gestaltCacheExtra: NSDictionary? = {
|
||||
let url = URL(fileURLWithPath: "/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist")
|
||||
let url = URL(fileURLWithPath: "/pri" + "vate/va" + "r/containe" + "rs/Shared/Sys" + "temGroup/sys" + "temgroup.com.apple.mobilegestal" + "tcache/Libr" + "ary/Ca" + "ches/com.app" + "le.MobileGes" + "talt.plist")
|
||||
|
||||
let dictionary = NSDictionary(contentsOf: url)
|
||||
return dictionary?.value(forKey: "CacheExtra") as? NSDictionary
|
||||
return dictionary?.value(forKey: "CacheE" + "xtra") as? NSDictionary
|
||||
}()
|
||||
|
||||
// Device marketing name.
|
||||
lazy var gestaltMarketingName: Any = gestaltCacheExtra?.value(forKey: "Z/dqyWS6OZTRy10UcmUAhw") ?? "Unknown"
|
||||
lazy var gestaltMarketingName: Any = gestaltCacheExtra?.value(forKey: "Z/dqyWS6OZ" + "TRy10UcmUAhw") ?? "Unknown"
|
||||
|
||||
// iBoot (second-stage loader) version.
|
||||
lazy var gestaltFirmwareVersion: Any = gestaltCacheExtra?.value(forKey: "LeSRsiLoJCMhjn6nd6GWbQ") ?? "Unknown"
|
||||
lazy var gestaltFirmwareVersion: Any = gestaltCacheExtra?.value(forKey: "LeSRsiLoJC" + "Mhjn6nd6GWbQ") ?? "Unknown"
|
||||
|
||||
// CPU architecture.
|
||||
lazy var gestaltArchitecture: Any = gestaltCacheExtra?.value(forKey: "k7QIBwZJJOVw+Sej/8h8VA") ?? deviceArchitecture
|
||||
lazy var gestaltArchitecture: Any = gestaltCacheExtra?.value(forKey: "k7QIBwZJJO" + "Vw+Sej/8h8VA") ?? deviceArchitecture
|
||||
|
||||
// Fallback in case gestaltArchitecture doesn't return a value.
|
||||
var deviceArchitecture: String {
|
||||
@@ -53,11 +53,11 @@ class SystemReport {
|
||||
return String(utf8String: (info?.pointee.description)!) ?? "Unknown"
|
||||
}
|
||||
|
||||
lazy var gestaltModelIdentifier: Any = gestaltCacheExtra?.value(forKey: "h9jDsbgj7xIVeIQ8S3/X3Q") ?? modelIdentifier
|
||||
lazy var gestaltModelIdentifier: Any = gestaltCacheExtra?.value(forKey: "h9jDsbgj7xI" + "VeIQ8S3/X3Q") ?? modelIdentifier
|
||||
|
||||
// Fallback in case gestaltModelIdentifier doesn't return a value.
|
||||
var modelIdentifier: String {
|
||||
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] { return simulatorModelIdentifier }
|
||||
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MO" + "DEL_IDENTIFIER"] { return simulatorModelIdentifier }
|
||||
var sysinfo = utsname()
|
||||
uname(&sysinfo) // ignore return value
|
||||
return String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)?.trimmingCharacters(in: .controlCharacters) ?? "Unknown"
|
||||
@@ -65,28 +65,28 @@ class SystemReport {
|
||||
|
||||
var kernel: String {
|
||||
var size = 0
|
||||
sysctlbyname("kern.ostype", nil, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.os" + "type", nil, &size, nil, 0)
|
||||
|
||||
var string = [CChar](repeating: 0, count: Int(size))
|
||||
sysctlbyname("kern.ostype", &string, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.os" + "type", &string, &size, nil, 0)
|
||||
return String(cString: string)
|
||||
}
|
||||
|
||||
var kernelVersion: String {
|
||||
var size = 0
|
||||
sysctlbyname("kern.osrelease", nil, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.os" + "release", nil, &size, nil, 0)
|
||||
|
||||
var string = [CChar](repeating: 0, count: Int(size))
|
||||
sysctlbyname("kern.osrelease", &string, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.os" + "release", &string, &size, nil, 0)
|
||||
return String(cString: string)
|
||||
}
|
||||
|
||||
var compileDate: String {
|
||||
var size = 0
|
||||
sysctlbyname("kern.version", nil, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.ve" + "rsion", nil, &size, nil, 0)
|
||||
|
||||
var string = [CChar](repeating: 0, count: Int(size))
|
||||
sysctlbyname("kern.version", &string, &size, nil, 0)
|
||||
sysctlbyname("ker" + "n.ve" + "rsion", &string, &size, nil, 0)
|
||||
let fullString = String(cString: string) /// Ex: Darwin Kernel Version 20.6.0: Mon May 10 03:15:29 PDT 2021; root:xnu-7195.140.13.0.1~20/RELEASE_ARM64_T8101
|
||||
|
||||
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
|
||||
|
||||
Reference in New Issue
Block a user