Posts

Post not yet marked as solved
1 Replies
I also have the same question here.
Post marked as solved
2 Replies
Again, I found it myself. override func windowDidLoad() { super.windowDidLoad() window?.level = .floating }
Post not yet marked as solved
1 Replies
I did much search on the web and found no answer. The only clue to my question is that I have use another "template" tableview that is identical to the inner tableview. Finally I devised a working solution, I posted here for the purpose of helping others who may have the same/similar problem. Note there is a little trick about calculating the actually row height - you have to include height of the template tableview's header view. func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat { if tableView is DetailsTableView { return tableView.rowHeight } if let rowHeight = rowHeights[row] { return rowHeight } let item = categorizedItems[row] if sizingTableView == nil { var objects: NSArray! detailsNib!.instantiate(withOwner: nil, topLevelObjects: &objects) for object in objects { if let sizingCellView = object as? DetailsCellView { sizingTableView = sizingCellView.tableView sizingTableView.delegate = self sizingTableView.dataSource = self sizingTableView.isHidden = true sizingTableView.enclosingScrollView?.setFrameSize(NSSize.zero) self.view.addSubview(sizingTableView) break } } } print("<", sizingTableView.bounds.height) sizingTableView.row = row sizingTableView.files = item.files sizingTableView.reloadData() print(">", sizingTableView.bounds.height) var rowHeight = sizingTableView.bounds.height + (sizingTableView.headerView?.bounds.height ?? 0.0) + summaryTableView.intercellSpacing.height rowHeights[row] = rowHeight if rowHeight > MaxRowHeight { let trailingHeight = (sizingTableView.bounds.height / Double(item.files.count)) * Double(TrailingRowCount) if rowHeight - MaxRowHeight < trailingHeight { // Try reserve height for some rows to let scrollbar look more natural. rowHeight = max(0, MaxRowHeight - trailingHeight) } else { rowHeight = MaxRowHeight } } return max(rowHeight, summaryTableView.rowHeight) }
Post not yet marked as solved
1 Replies
I found a solution myself, after days of researching and experiments. It's related to another question "How tell if NSTableView is already displaying its last row entirely?". // For the inner tableview's scrollview class MyScrollView: NSScrollView { var onScrollWheel: EventHandler! override func scrollWheel(with event: NSEvent) { super.scrollWheel(with: event) // In onScrollWheel, determine if vertical scrollbar is hidden or at top/bottom, // then delegate this event to the outer tableview: // outerTableView.scrollWheel(with: event) onScrollWheel?(event) } }
Post not yet marked as solved
2 Replies
At last, I found the answer myself, again. The answer lies in NSScroller.floatValue which inherited from NSControl. let shouldDelegate = { if let scroller = scrollView.verticalScroller { return scroller.isHidden || scroller.floatValue == 0 || scroller.floatValue == 1.0 } return true }()
Post marked as solved
4 Replies
Maybe my question is too general. I have a need to calculate all width of an NSTableView. However, I found that total width of all columns is far less than NSTableView.bound.width: let width = tableView.tableColumns.reduce(0) { ac, col in ac + col.width } print(width, tableView.bounds.width) This is true even I manually adjust last column so that it fills the gap between the column and tableview right border. -----------| <- table right border last column| -----------| So I assume NSTableColumn.width and NSView.bounds.width are using different units.
Post not yet marked as solved
1 Replies
Ah, I found out why, though it's a little embarrassing... After so many years, I realized that NSApp.mainWindow is dynamic - it can be any window that has the key focus! I have to create a global variable to keep instance of the initial window (as designed in main storyboard). var mainWindow: NSWindow? // Set in MainWC.windowDidLoad class AnotherWC: NSWindowController, NSWindowDelegate { override func windowDidLoad() { super.windowDidLoad() print(self.className, #function) } func windowShouldClose(_ sender: NSWindow) -> Bool { print(self.className, #function) return true } func windowWillClose(_ notification: Notification) { print(self.className, #function) print(mainWindow ?? "mainWindow is nil") mainWindow?.orderFront(nil) } }
Post marked as solved
2 Replies
Anyway, I found out the cause. The error is thrown when initializing XMLDocument with an empty string: let xd = try XMLDocument(xmlString: "")
Post not yet marked as solved
2 Replies
Xcode (starting from version 11, maybe) is buuuuuuuuuuuggy. It has big features, but fails miserably on small or tiny functionalities. You can try right-click the file and select "View As->Source Code". If this does not work either, then remove the file and re-add it back (at least this worked for me once or twice).
Post not yet marked as solved
1 Replies
I believe you have to create localizable.strings resource file in FR locale. The String.init and runtime do not do the magic translation work.
Post marked as solved
2 Replies
Unfortunately, contentsOfDirectory(at:) suffers the same problem as the enumerator. But anyway, I devised a workaround that leads to a little more memory footprint. var fileList = [URL]() for case let fileURL as URL in it { fileList.append(fileURL) } // Sort by length so that the shortest comes first fileList.sort { $0.relativePath.count < $1.relativePath.count } // The shortest path without last component is the root path. let packageRoot = fileList[0].url.deletingLastPathComponent().path
Post marked as solved
5 Replies
It seems my conclusion about a single channel cannot handle multiple reading operations is not correct. Here is a much improved sample to demonstrate the final solution: import Cocoa let OneMebi = 1024 * 1024 class IoJobState { let name: String let signature: UInt8 let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var dispatchDone = false var dispatchData: DispatchData? var dispatchError: Int32 = 0 init(_ name: String, label: NSTextField, signature: UInt8) { self.name = name self.label = label self.signature = signature } } class DispatchIoVC: NSViewController { @IBOutlet weak var label: NSTextField! @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath label.stringValue = filePath var fh: Int32 = -1 let signatures: [UInt8] = [0x11, 0xef] repeat { fh = open(filePath, O_RDONLY) if fh >= 0 { break } fh = open(filePath, O_WRONLY | O_CREAT) if (fh < 0) { perror("Opening") label1.stringValue = "Unable to create file" return } let a = [UInt8](repeating: signatures[0], count: OneMebi) a.withUnsafeBytes { for _ in 0..<100 { write(fh, $0.baseAddress, a.count) } } let a2 = [UInt8](repeating: signatures[1], count: OneMebi) a2.withUnsafeBytes { for _ in 0..<90 { write(fh, $0.baseAddress, a.count) } write(fh, $0.baseAddress, 13) } close(fh) filePath.utf8CString.withUnsafeBytes { if 0 != chmod($0.baseAddress, 0o644) { perror("chmod") } } } while true let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1, signature: signatures[0]), IoJobState("job2", label: label2, signature: signatures[1])] jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max let channel = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("Good cleanup") } else { print("Bad cleanup \(error)") } close(fh) if unlink(filePath) == 0 { print("File successfully deleted") } else { perror("unlink") } } jobs.forEach { job in channel.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.dispatchDone = done job.dispatchData = data job.dispatchError = error job.eof = job.dispatchDone && job.dispatchError == 0 && (job.dispatchData == nil || job.dispatchData!.isEmpty) if let data = job.dispatchData { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(with: job) } if let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } Thread.sleep(forTimeInterval: TimeInterval.random(in: 0.01..<0.05)) if !job.buffer.allSatisfy({ $0 == job.signature }) { print("\(job.name): bad reading \(job.total)") } } } } func upateUI(with job: IoJobState) { if job.dispatchDone && !job.done { job.done = true print("\(job.name) done") } if let data = job.dispatchData { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name): got \(byteString) bytes, total \(totalString) \(job.total)" } if job.dispatchError != 0 { print("\(job.name): got error code:", job.dispatchError) perror("") } } } }
Post marked as solved
5 Replies
Here is my experiment with DispatchIO, hope it may be helpful for those who are looking for a quick start. Note that - The code still uses one channel (DispatchIO object) for reading. The result is that most of the cases 2nd part finishes and reaches EOF and thus causes the first part not having chance to execute cleanup handler. import Cocoa class IoJobState { let name: String! let label: NSTextField! var total: UInt64 = 0 var eof = false var offset: off_t = 0 var length = 0 var buffer = Data() var done = false var data: DispatchData? var error: Int32 = 0 init(_ name: String, label: NSTextField) { self.name = name self.label = label } } class DispatchIoVC: NSViewController { @IBOutlet weak var label1: NSTextField! @IBOutlet weak var label2: NSTextField! override func viewDidLoad() { super.viewDidLoad() } let opQ = OperationQueue() /// Should not be used! let globalDispatchQ = DispatchQueue.global() /// Note: the .concurrent attribute is a must, otherwise the created queue is in serial mode. let dispatchQ = DispatchQueue(label: "test", qos: .utility, attributes: .concurrent) @IBAction func test_click(_ sender: Any) { let filePath = ("~/tmp/bigfile" as NSString).expandingTildeInPath print(filePath) let fh = open(filePath, O_RDONLY) if fh < 0 { let proc = Process() proc.executableURL = URL(fileURLWithPath: "/usr/bin/truncate") proc.arguments = ["-s", "5G", filePath] do { try proc.run() label1.stringValue = "Created \(filePath) of size 5GB, try again" } catch { print(error) } return } let fmt = ByteCountFormatter() fmt.countStyle = .binary let size = lseek(fh, 0, SEEK_END) print("size:", fmt.string(fromByteCount: size)) lseek(fh, 0, SEEK_SET) let jobs = [IoJobState("job1", label: label1), IoJobState("job2", label: label2)] // TODO: A single channel cannot be used to issue multiple read operation? let d = DispatchIO(type: .random, fileDescriptor: fh, queue: dispatchQ) { error in if error == 0 { print("All good") } else { print("Got error code:", error) } if jobs[0].eof && jobs[1].eof { print("Closing file") close(fh) try? FileManager.default.removeItem(atPath: filePath) } } opQ.addOperation { jobs[0].offset = 0 jobs[0].length = Int(size) / 2 jobs[1].offset = off_t(jobs[0].length) jobs[1].length = .max jobs.forEach { job in d.read(offset: job.offset, length: job.length, queue: self.dispatchQ) { done, data, error in job.done = done job.data = data job.error = error job.eof = job.done && job.error == 0 && (job.data == nil || job.data!.isEmpty) if let data = job.data { job.total += UInt64(data.count) } OperationQueue.main.addOperation { upateUI(job: job) } if !job.eof, let data { job.buffer.reserveCapacity(data.count) job.buffer.withUnsafeMutableBytes { _ = data.copyBytes(to: $0) } } } } } func upateUI(job: IoJobState ) { if job.eof { print("\(job.name!) done") } if let data = job.data { let byteString = fmt.string(fromByteCount: Int64(data.count)) let totalString = fmt.string(fromByteCount: Int64(job.total)) job.label.stringValue = "\(job.name!): got \(byteString) bytes, total \(totalString)" } if job.error != 0 { print("\(job.name!): got error code:", job.error) perror("") } } } }
Post marked as solved
5 Replies
You just need multiple channels. This drives away the cloud in my mind. Based on what you describe, I believe a channel (wrapped as a DispathIO) is efficient and cheap to construct, which was what I worried about.
Post not yet marked as solved
1 Replies
Yeah, I solved it myself. I removed this file from project and re-added it back. Now it works fine.