Post

Replies

Boosts

Views

Activity

Reply to NavigationSplitView + FetchResults
It is managed by CoreData: I'm using the id of the object, but previously I was using a field "itemID" as a UUID, but it was the same behaviour. It will be something like: @objc(Item) public class Item: NSManagedObject { } extension Item { @nonobjc public class func fetchRequest() -> NSFetchRequest<Item> { return NSFetchRequest<Item>(entityName: "Item") } @NSManaged public var itemId: UUID? @NSManaged public var name: String? @NSManaged public var timestamp: Date? } extension Item : Identifiable { } I have added an extension to set the id, but it is still the same: extension Item { public var id: UUID { itemId ?? UUID() } } Thanks,
Feb ’23
Reply to View like the camera app
Hello, I implemented a solution that fits my requirements. If you are interested on it, here is the code: https://github.com/heltena/Orientation Basically, I'm showing a sheet using UIKit, and controlling the supportedInterfaceOrientations in the UIViewController that is shown. For lazy people, here is the code: // // RestrictedInterfaceOrientationSheet.swift // Orientation // // Created by Heliodoro Tejedor Navarro on 1/3/23. // import SwiftUI public enum ModalTransitionStyle: Int, @unchecked Sendable { case coverVertical = 0 case flipHorizontal = 1 case crossDissolve = 2 fileprivate var uiKitVersion: UIModalTransitionStyle { return UIModalTransitionStyle(rawValue: self.rawValue)! } } struct RestrictedInterfaceOrientationSheet<SheetContent: View>: UIViewControllerRepresentable { @Binding var isPresented: Bool var restrictInterfaceOrientationTo: UIInterfaceOrientationMask var modalTransitionStyle: ModalTransitionStyle var animated: Bool var content: () -> SheetContent func makeUIViewController(context: Context) -> InternalViewController { InternalViewController() } func updateUIViewController(_ uiViewController: InternalViewController, context: Context) { if !isPresented { uiViewController.presentedViewController?.dismiss(animated: animated) return } if uiViewController.presentedViewController == nil { let sheetViewController = PortraitHostingViewController(restrictInterfaceOrientationTo: restrictInterfaceOrientationTo, rootView: content()) sheetViewController.modalPresentationStyle = .overFullScreen // don't use .fullscreen, so it will remove the parent from the view hierarchy! sheetViewController.modalTransitionStyle = modalTransitionStyle.uiKitVersion uiViewController.present(sheetViewController, animated: animated) return } if let presentedViewController = uiViewController.presentedViewController as? UIHostingController<SheetContent> { presentedViewController.rootView = content() return } fatalError("Invalid parent when dismissing a presented view controller") } class InternalViewController: UIViewController { override func loadView() { super.loadView() view.backgroundColor = .clear } } class PortraitHostingViewController<Content: View>: UIHostingController<Content> { var restrictInterfaceOrientationTo: UIInterfaceOrientationMask init(restrictInterfaceOrientationTo: UIInterfaceOrientationMask, rootView: Content) { self.restrictInterfaceOrientationTo = restrictInterfaceOrientationTo super.init(rootView: rootView) } @MainActor required dynamic init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { restrictInterfaceOrientationTo } } } struct RestrictedInterfaceOrientationSheetViewModifier<SheetContent: View>: ViewModifier { @Binding var isPresented: Bool var restrictInterfaceOrientationTo: UIInterfaceOrientationMask var modalTransitionStyle: ModalTransitionStyle var animated: Bool @ViewBuilder var content: () -> SheetContent func body(content: Content) -> some View { content .background { RestrictedInterfaceOrientationSheet( isPresented: $isPresented, restrictInterfaceOrientationTo: restrictInterfaceOrientationTo, modalTransitionStyle: modalTransitionStyle, animated: animated, content: self.content) } } } extension View { public func sheet<SheetContent: View>(isPresented: Binding<Bool>, restrictInterfaceOrientationTo: UIInterfaceOrientationMask = .portrait, modalTransitionStyle: ModalTransitionStyle = .crossDissolve, animated: Bool = true, onDismiss: (() -> Void)?, @ViewBuilder content: @escaping () -> SheetContent) -> some View { self.modifier( RestrictedInterfaceOrientationSheetViewModifier( isPresented: isPresented, restrictInterfaceOrientationTo: restrictInterfaceOrientationTo, modalTransitionStyle: modalTransitionStyle, animated: animated, content: content)) } } And how to use it: struct ContentView: View { @Binding var document: OrientationDocument @State var showCamera = false var body: some View { Form { Section { Text(document.text) } header: { Text("Example") } } .toolbar { Button { showCamera = true } label: { Label("Camera", systemImage: "camera") } } .orientationLockModifier(mask: .portrait) .sheet(isPresented: $showCamera, restrictInterfaceOrientationTo: .portrait, modalTransitionStyle: .crossDissolve, animated: false) { print("bye bye") } content: { VStack(spacing: 20) { Spacer() Text("Show Camera Preview here") Button { showCamera = false } label: { Text("Close") } Spacer() } } } }
Jan ’23
Reply to Parser Json with Single Quote
Before replacing the single to double quotes, check there are no double quotes inside the strings: from: {'a': 'hello my "friend"!'} to: {"a": "hello my "friend"!"} contains an error! Is it possible to change the source of your json or run an script before generating it? It looks like the json is coming from a call to a print function inside a python code (or similar language). If this is the case, try using the json package to dump the data in the correct format. Best,
Dec ’22
Reply to UIActivityView + Video --> Couldn't send message
Hello! Yes, I'm using UIActivityViewController inside the ActivityView, which is a UIViewControllerRepresentable. I did some tests and I realized that the video, for some reason I don't understand yet, is not correct to publish, not only in Whatsapp, but Twitter as well. Because I cannot publish the video using the Twitter website, I think the problem is not the sharing (UIActivityViewController), but the video itself. The console: Start the app, click "Generate and Share", and show the Share Sheet: 2021-08-12 15:35:33.728845-0500 TestingEarthtunesVideo[7630:3804560] [Warning] Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a table view cell's content view. We're considering the collapse unintentional and using standard height instead. Cell: <SwiftUI.ListCoreCellHost: 0x15c80f000; baseClass = UITableViewCell; frame = (0 216.667; 343 44); clipsToBounds = YES; autoresize = W; layer = <CALayer: 0x281e8e5c0>> TemperaryFolder: file:///private/var/mobile/Containers/Data/Application/C12414B3-3120-43B8-92E5-8533512219EC/tmp/C52ECFEA-A0F3-49AB-B00D-24D8DE4807E1 2021-08-12 15:35:37.234098-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD HEVC codec registered 2021-08-12 15:35:37.234374-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD H264 codec registered 2021-08-12 15:35:37.234516-0500 TestingEarthtunesVideo[7630:3804790] AVDRegister - AppleAVD Leghorn codec registered 2021-08-12 15:35:39.662574-0500 TestingEarthtunesVideo[7630:3804560] [User Defaults] Couldn't read values in CFPrefsPlistSource<0x2830dfa80> (Domain: com.apple.Sharing, User: kCFPreferencesCurrentUser, ByHost: No, Container: kCFPreferencesNoContainer, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access 2021-08-12 15:35:39.663361-0500 TestingEarthtunesVideo[7630:3804560] [User Defaults] Couldn't read values in CFPrefsPlistSource<0x2830dfa80> (Domain: com.apple.Sharing, User: kCFPreferencesCurrentUser, ByHost: No, Container: kCFPreferencesNoContainer, Contents Need Refresh: Yes): accessing preferences outside an application's container requires user-preference-read or file-read-data sandbox access 2021-08-12 15:35:39.666559-0500 TestingEarthtunesVideo[7630:3804560] <CATransformLayer: 0x281e71f80> - changing property backgroundColor in transform-only layer, will have no effect 2021-08-12 15:35:39.790171-0500 TestingEarthtunesVideo[7630:3804560] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.  Try this:  (1) look at each constraint and try to figure out which you don't expect;  (2) find the code that added the unwanted constraint or constraints and fix it.  (     "<NSLayoutConstraint:0x283d82da0 _UIActivityActionCellTitleLabel:0x15d0db510.height >= 22.6667   (active)>",     "<NSLayoutConstraint:0x283d93cf0 V:|-(15)-[_UIActivityActionCellTitleLabel:0x15d0db510]   (active, names: '|':UIView:0x15d0dc110 )>",     "<NSLayoutConstraint:0x283d93d40 V:[_UIActivityActionCellTitleLabel:0x15d0db510]-(15)-|   (active, names: '|':UIView:0x15d0dc110 )>",     "<NSLayoutConstraint:0x283d65a90 'UIView-Encapsulated-Layout-Height' UIView:0x15d0dc110.height == 52   (active)>" ) Will attempt to recover by breaking constraint  <NSLayoutConstraint:0x283d82da0 _UIActivityActionCellTitleLabel:0x15d0db510.height >= 22.6667   (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. 2021-08-12 15:35:39.790711-0500 TestingEarthtunesVideo[7630:3804560] [LayoutConstraints] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.  Try this:  (1) look at each constraint and try to figure out which you don't expect;  (2) find the code that added the unwanted constraint or constraints and fix it.  (     "<NSLayoutConstraint:0x283d6de00 _UIActivityActionCellTitleLabel:0x15d0e5170.height >= 22.6667   (active)>",     "<NSLayoutConstraint:0x283d6d950 V:|-(15)-[_UIActivityActionCellTitleLabel:0x15d0e5170]   (active, names: '|':UIView:0x15d0e5d70 )>",     "<NSLayoutConstraint:0x283d6d9a0 V:[_UIActivityActionCellTitleLabel:0x15d0e5170]-(15)-|   (active, names: '|':UIView:0x15d0e5d70 )>",     "<NSLayoutConstraint:0x283d65e00 'UIView-Encapsulated-Layout-Height' UIView:0x15d0e5d70.height == 52   (active)>" ) Will attempt to recover by breaking constraint  <NSLayoutConstraint:0x283d6de00 _UIActivityActionCellTitleLabel:0x15d0e5170.height >= 22.6667   (active)> Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful. 2021-08-12 15:36:08.766396-0500 TestingEarthtunesVideo[7630:3805356] [WindowServer] display_timer_callback: unexpected state (now:8beca030708 < expected:8beca09199d) At this point, when I'm selecting Whatsapp and the group, and click the send button, the error is presented, but nothing appears in the console. To show the process of the video creation, I did a new repo with the code I'm using to generate the video and a little explanation in the README.md (copy & paste): This is a repo to explain the error when uploading a video to social networks (twitter and whatsapp). Try to upload this video to your twitter account: << check the repo, I cannot post videos here >> Error in Twitter: Error in Whatsapp: If I'm running in the terminal: $ ffmpeg -i Media/earthtunes.mp4 Media/earthtunes-ffmpeg.mp4 It is possible to post the new video on Twitter... (probably it will work on Whatsapp as well) I appreciate your feedback!!! https://github.com/heltena/TestingEarthtunesVideo Thanks!!!!!
Aug ’21
Reply to How can I prevent a document-based swiftUI app from saving files?
I just removed the menu: import SwiftUI @main struct FlowApp: App {     var body: some Scene {         DocumentGroup(viewing: FlowDocument.self) { file in             ContentView(document: file.$document)         }         .windowStyle(HiddenTitleBarWindowStyle())         .commands {             SidebarCommands()             CommandGroup(replacing: .saveItem) { } //// <<<<< HERE         }     } } Not sure if this is what you are looking for. Also, added an error: func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { throw CocoaError(.fileWriteNoPermission) } But, yes, I'm missing that the DocumentGroup does that internally.
Jul ’21
Reply to AVFoundation / C5 note is not playing properly
I changed AVAudioPlayerNode by AVAudioSourceNode (check the BuildingASignalGenerator example). My new code for your records: import AVFoundation import Combine class ContentViewModel: ObservableObject { &#9;&#9;let audioEngine: AVAudioEngine &#9;&#9;var sourceNode: AVAudioSourceNode? &#9;&#9;let downMixer: AVAudioMixerNode &#9;&#9;let data: [Float] &#9;&#9;let sampleRate: Float &#9;&#9; &#9;&#9;init() { &#9;&#9;&#9;&#9;let sinFrequency: Float = 523.25&#9;/* C5 */ &#9;&#9;&#9;&#9;let sampleRate: Float = 44100 &#9;&#9;&#9;&#9;let seconds: Float = 10 &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;let range = 0 ..< Int(seconds * sampleRate) &#9;&#9;&#9;&#9;self.data = range.map { sin(2.0 * .pi * Float($0) * sinFrequency / 44100) } &#9;&#9;&#9;&#9;self.sampleRate = sampleRate &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;audioEngine = AVAudioEngine() &#9;&#9;&#9;&#9;downMixer = AVAudioMixerNode() &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;let _ = audioEngine.mainMixerNode &#9;&#9;&#9;&#9;audioEngine.prepare() &#9;&#9;&#9;&#9;try! audioEngine.start() &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;audioEngine.attach(downMixer) &#9;&#9;&#9;&#9;audioEngine.connect(downMixer, to: audioEngine.mainMixerNode, format: nil) &#9;&#9;} &#9; &#9;&#9;var isPlaying: Bool { sourceNode != nil } &#9;&#9;@Published private(set) var totalSeconds: Float = 0 &#9;&#9;@Published private(set) var currentTime: Float = 0 &#9;&#9; &#9;&#9;func stopCurrent() { &#9;&#9;&#9;&#9;if let sourceNode = sourceNode { &#9;&#9;&#9;&#9;&#9;&#9;audioEngine.detach(sourceNode) &#9;&#9;&#9;&#9;&#9;&#9;self.sourceNode = nil &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;self.totalSeconds = 0 &#9;&#9;&#9;&#9;self.currentTime = 0 &#9;&#9;&#9;&#9;self.objectWillChange.send() &#9;&#9;} &#9;&#9; &#9;&#9;func play(speedUpFactor: Float) { &#9;&#9;&#9;&#9;let playingSampleRate = sampleRate * speedUpFactor &#9;&#9;&#9;&#9;guard &#9;&#9;&#9;&#9;&#9;&#9;let dataFormat = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: Double(playingSampleRate), channels: 1, interleaved: false) &#9;&#9;&#9;&#9;else { &#9;&#9;&#9;&#9;&#9;&#9;fatalError() &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;stopCurrent() &#9;&#9; &#9;&#9;&#9;&#9;self.totalSeconds = Float(data.count) / Float(playingSampleRate) &#9;&#9;&#9;&#9;self.currentTime = 0 &#9;&#9;&#9;&#9;var index: Int = 0 &#9;&#9;&#9;&#9;let sourceNode = AVAudioSourceNode(format: dataFormat) { [self] _, audioTimeStamp, frameCount, audioBufferList -> OSStatus in &#9;&#9;&#9;&#9;&#9;&#9;let ablPointer = UnsafeMutableAudioBufferListPointer(audioBufferList) &#9;&#9;&#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;&#9;&#9;let remainValues = min(Int(frameCount), data.count - index) &#9;&#9;&#9;&#9;&#9;&#9;for frame in 0 ..< remainValues { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;let value = index < data.count ? data[index] : 0.0 &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;index += 1 &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;for buffer in ablPointer { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;buf[frame] = value &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;&#9;&#9;DispatchQueue.main.async { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;self.currentTime += Float(remainValues) / Float(playingSampleRate) &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;if remainValues < Int(frameCount) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;for frame in remainValues ..< Int(frameCount) { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;for buffer in ablPointer { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;let buf: UnsafeMutableBufferPointer<Float> = UnsafeMutableBufferPointer(buffer) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;buf[frame] = 0.0 &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;DispatchQueue.main.async { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stopCurrent() &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;return noErr &#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;self.sourceNode = sourceNode &#9;&#9;&#9;&#9;audioEngine.attach(sourceNode) &#9;&#9;&#9;&#9;audioEngine.connect(sourceNode, to: downMixer, format: nil) &#9;&#9;&#9;&#9;self.objectWillChange.send() &#9;&#9;} } And the SwiftUI view: import SwiftUI struct ContentView: View { &#9;&#9;@StateObject var viewModel = ContentViewModel() &#9;&#9; &#9;&#9;var body: some View { &#9;&#9;&#9;&#9;VStack { &#9;&#9;&#9;&#9;&#9;&#9;Spacer() &#9;&#9;&#9;&#9;&#9;&#9;HStack { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Button("Play x1") { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;viewModel.play(speedUpFactor: 1) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Button("Play x2") { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;viewModel.play(speedUpFactor: 2) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Button("Stop") { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;viewModel.stopCurrent() &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;if viewModel.isPlaying { &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Text("Playing: \(viewModel.currentTime) of \(viewModel.totalSeconds)") &#9;&#9;&#9;&#9;&#9;&#9;} &#9;&#9;&#9;&#9;&#9;&#9;Spacer() &#9;&#9;&#9;&#9;} &#9;&#9;} } struct ContentView_Previews: PreviewProvider { &#9;&#9;static var previews: some View { &#9;&#9;&#9;&#9;ContentView() &#9;&#9;} }
Oct ’20