Add ability to record screen although not 100% satisfied with it.

This commit is contained in:
Casey Fleser 2022-08-21 07:24:49 -05:00
parent 7b02bd3da2
commit 4d67317ebb
4 changed files with 93 additions and 20 deletions

View file

@ -8,14 +8,24 @@
import Foundation
struct SimCtl {
func run(args: [String]) throws -> Data {
func run(args: [String], run: Bool = true) throws -> Process {
let process = Process()
let pipe = Pipe()
process.executableURL = URL(fileURLWithPath: "/usr/bin/xcrun")
process.arguments = ["simctl"] + args
process.standardOutput = pipe
process.standardError = nil
if run {
try process.run()
}
return process
}
func run(args: [String]) throws -> Data {
let process : Process = try run(args: args, run: false)
let pipe = Pipe()
process.standardOutput = pipe
try process.run()
return pipe.fileHandleForReading.readDataToEndOfFile()
@ -119,4 +129,8 @@ struct SimCtl {
func saveScreen(_ device: SimDevice, url: URL) throws {
try runAsync(args: ["io", device.udid, "screenshot", url.path])
}
func saveVideo(_ device: SimDevice, url: URL) throws -> Process {
return try run(args: ["io", device.udid, "recordVideo", "--force", url.path])
}
}

View file

@ -27,6 +27,8 @@ class SimDevice: ObservableObject, Decodable {
@Published var appearance = Appearance.unknown
@Published var contentSize = ContentSize.unknown
@Published var increaseContrast = IncreaseContrast.unknown
@Published var isRecording = false
var recordingProcess : Process?
var isTransitioning : Bool { state == .booting || state == .shuttingDown }
var isBooted : Bool {
get { state.showBooted == true }
@ -190,6 +192,28 @@ class SimDevice: ObservableObject, Decodable {
print("Failed to save screen: \(error)")
}
}
func saveVideo(_ url: URL) {
do {
recordingProcess = try SimCtl().saveVideo(self, url: url)
if recordingProcess != nil {
isRecording = true
}
} catch {
print("Failed to save video: \(error)")
}
}
func endRecording() {
if let process = recordingProcess {
process.interrupt()
recordingProcess = nil
isRecording = false
}
else {
isRecording = false
}
}
}
extension SimDevice {

View file

@ -6,6 +6,7 @@
//
import SwiftUI
import UniformTypeIdentifiers
extension SimDevice {
public var content : some View { DeviceContent(self) }
@ -45,6 +46,25 @@ extension SimDevice.IncreaseContrast: ToggleDescriptor {
}
struct DeviceContent: View {
enum SaveType {
case image
case video
var allowedContentTypes : [UTType] {
switch self {
case .image: return [.png]
case .video: return [.mpeg4Movie]
}
}
var title : String {
switch self {
case .image: return "Save Screen"
case .video: return "Save Recording"
}
}
}
@ObservedObject var device : SimDevice
@State var isBooted : Bool
@ -77,6 +97,23 @@ struct DeviceContent: View {
.font(.subheadline)
.textSelection(.enabled)
ContentHeader("Actions")
HStack(spacing: 16) {
Button(action: { saveScreen(.image) }) {
Text("Save Screen")
.fontWeight(.semibold)
.font(.system(size: 11))
}
.buttonStyle(.systemIcon("camera.on.rectangle"))
Button(action: { device.isRecording ? device.endRecording() : saveScreen(.video) }) {
Text(device.isRecording ? "End Recording" : "Record Screen")
.fontWeight(.semibold)
.font(.system(size: 11))
}
.buttonStyle(.systemIcon("record.circle", active: device.isRecording))
}
ContentHeader("UI")
HStack(spacing: 16) {
if device.appearance != .unsupported {
@ -99,14 +136,6 @@ struct DeviceContent: View {
.opacity(isBooted ? 1.0 : 0.5)
}
}
ContentHeader("Actions")
Button(action: saveScreen) {
Text("Save Screen")
.fontWeight(.semibold)
.font(.system(size: 11))
}
.buttonStyle(.systemIcon("camera.on.rectangle"))
}
.environment(\.isEnabled, isBooted)
.onAppear {
@ -124,21 +153,24 @@ struct DeviceContent: View {
}
}
}
func saveScreen() {
func saveScreen(_ type: SaveType = .image) {
let savePanel = NSSavePanel()
savePanel.allowedContentTypes = [.png]
savePanel.allowedContentTypes = type.allowedContentTypes
savePanel.canCreateDirectories = true
savePanel.isExtensionHidden = false
savePanel.title = "Save Screen"
savePanel.title = type.title
savePanel.message = "Select destination"
savePanel.nameFieldLabel = "Filename:"
savePanel.nameFieldStringValue = "\(device.name) - \(fileDateFormatter.string(from: Date()))"
if savePanel.runModal() == .OK {
if let url = savePanel.url {
device.saveScreen(url)
switch type {
case .image: device.saveScreen(url)
case .video: device.saveVideo(url)
}
}
}
}

View file

@ -8,17 +8,19 @@
import SwiftUI
extension ButtonStyle where Self == SystemIconButtonStyle {
static func systemIcon(_ imageName: String) -> SystemIconButtonStyle {
SystemIconButtonStyle(imageName)
static func systemIcon(_ imageName: String, active: Bool = false) -> SystemIconButtonStyle {
SystemIconButtonStyle(imageName, active: active)
}
}
struct SystemIconButtonStyle: ButtonStyle {
@State var isFocused = false
let isActive : Bool
let imageName : String
init(_ imageName: String) {
init(_ imageName: String, active: Bool = false) {
self.imageName = imageName
self.isActive = active
}
func makeBody(configuration: Configuration) -> some View {
@ -32,11 +34,12 @@ struct SystemIconButtonStyle: ButtonStyle {
.resizable()
.aspectRatio(contentMode: ContentMode.fit)
.frame(width: 24, height: 24)
.foregroundColor(isActive ? .accentColor : foregroundColor(pressed: configuration.isPressed))
}
configuration.label
.foregroundColor(foregroundColor(pressed: configuration.isPressed))
}
.foregroundColor(foregroundColor(pressed: configuration.isPressed))
.padding(1.0)
.onHover { isFocused = $0 }
}