Post

Replies

Boosts

Views

Activity

Reply to How do I initialize a view models variables in a view
I moved the on appear to the h stack but the table rows portion of the table still ran before the on appear resulting in an index out of range error. So I added the suggested state variable and if statement. The view now works perfectly. Thank you again for providing great advice. Below is the updated code. struct DataTable: View { @ObservedObject var vm: ButtonsViewModel = ButtonsViewModel.shared var closingValues: [TradingDayClose] var heading: String = "" @State private var hasAppeared = false init(fundName: String, closingValues: [TradingDayClose]) { self.heading = fundName self.closingValues = closingValues } var body: some View { HStack { Spacer() .frame(width: 150) GroupBox(heading) { if hasAppeared { Table (of: TradingDayClose.self) { TableColumn("") { closingValue in Text(dateToStringFormatter.string(from: closingValue.timeStamp!)) .id(closingValue.timeStamp!) .textFormatting(fontSize: 14) .frame(width: 100, alignment: .center) } // end table column TableColumn("") { closingValue in Text(String(format: "$ %.2f", closingValue.close)) .textFormatting(fontSize: 14) .frame(width: 100, alignment: .center) } // end table column } rows: { ForEach((closingValues.indices), id: \.self) { index in if vm.showData[index] == true { TableRow(closingValues[index]) } } } .frame(width: 250) .overlay { let tempValue1: String = "Date" let tempValue2: String = "Closing Value" Text(tempValue1).position(x: 63, y: 15) .textFormatting(fontSize: 16) Text(tempValue2).position(x: 180, y: 15) .textFormatting(fontSize: 16) } } // end has appeared } // end group box .groupBoxStyle(Table2GroupBoxStyle()) Spacer() .frame(width: 50) VStack { Spacer() .frame(height: 100) ButtonUp25(closingValuesCount: closingValues.count) ButtonUp200(closingValuesCount: closingValues.count) Spacer() .frame(height: 20) ButtonDown25(closingValuesCount: closingValues.count) ButtonDown200(closingValuesCount: closingValues.count) Spacer() .frame(height: 100) } // end v stack Spacer() .frame(width: 50) } // end h stack .onAppear { vm.showData = [] vm.showData = Array(repeating: false, count: closingValues.count) vm.InitializeIndexes() vm.InitializeButtons() for i in vm.startIndex...vm.endIndex { DispatchQueue.main.asyncAfter(deadline: .now() + vm.renderRate * Double(i)) { vm.showData[i] = true } // end dispatch queue main async } hasAppeared = true } } // end body } // end struct
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jun ’23
Reply to How do I disable a button while it has focus
Closing value count variable holds the number of rows in the underlying data array. It is about 1200 rows. My table is set up to only show 25 rows from this array at a time. The 4 buttons, Page Up 200, Page Up 25, Page Down 25 and Page Down 200 allow the user to move up or down in the data. The start index variable holds the first row in the underlying array that will be displayed in the table. Upon initial display of the table, rows 0 thru 24 of the underlying array are displayed. If the user presses the Page Down 25 button the start index variable is now 25 and rows 25 thru 49 are displayed in the table. What I want to do is disable buttons that cannot do anything. For example if rows 0 thru 24 of the underlying array are being displayed then Page Up 25 and Page Up 200 should be disabled since there is no data above row 0 of the underlying array. In the logic I test to see if the start index variable is > 24. If so then the Page Up 25 button should no longer be disabled. Same for the Page Up 200 button. If the user has moved down through the underlying array and are now 200 or more rows from row 0 in the underlying array, then that button should no longer be disabled. In the initial if statement I am testing to see if I can no longer press the Page Down 200 button and fill the data table with 25 values, i.e. advancing 200 rows would place us closer to the last row in the underlying data array than 25 rows. In this case I want to disable the Page Down 200 button. I use the vm.pageDownDisabled200 (a boolean) to enable and disable the Page Down 200 button, which is a vew. If the value is true then the button should become disabled. It is used in the last line of the button view as .disabled(vm.pageDownDisabled200). This is where I seem to get into trouble. If the Page Down 200 button is enabled and has focus, trying to disable it appears to be the source of the error message "AttributeGraph: cycle detected through attribute 864480". But I could be wrong. Thank you for responding. Any insight you can provide will be appreciated.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jun ’23
Reply to How do I disable a button while it has focus
After some additional testing I am convinced that the error message is a result of disabling a button while it has focus. So I added a state variable of type boolean and toggle it in every button's action. Then I watch the state variable with an .onChange in the DataTable view. In the .onChange I test to see if a button should no longer be enabled and set the focus to a button that should still be enabled. At the end of this .onChange I toggle a second state variable of type boolean. Then I watch this state variable with a second .onChange. In the second .onChange I have logic that disables the appropriate buttons based on where the user is in the data being displayed. Doing it this way assures that no button that is being disabled has focus. Below are the 2 on change sections of code. .onChange(of: updateFocus, perform: { newValue in switch buttonWithFocus { case .btnUp200: if vm.startIndex - 200 <= 0 { buttonWithFocus = .btnUp25 } case .btnUp25: if vm.startIndex == 0 { buttonWithFocus = .btnDn25 } case .btnDn25: if vm.endIndex == closingValues.count - 1 { buttonWithFocus = .btnUp25 } case .btnDn200: if (closingValues.count - 1) - (vm.startIndex + 200) < 25 { buttonWithFocus = .btnDn25 } case .none: print("no button has focus") } updateEnabledButtons.toggle() }) .onChange(of: updateEnabledButtons) { newValue in if vm.startIndex + 25 <= closingValues.count - 1 { vm.pageDownDisabled25 = false } else { vm.pageDownDisabled25 = true } if (closingValues.count - 1) - (vm.startIndex + 200) >= 25 { vm.pageDownDisabled200 = false } else { vm.pageDownDisabled200 = true } if vm.startIndex > 0 { vm.pageUpDisabled25 = false } else { vm.pageUpDisabled25 = true } if vm.startIndex - 200 >= 0 { vm.pageUpDisabled200 = false } else { vm.pageUpDisabled200 = true } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jun ’23
Reply to How do I extract HTML table data from a string with regex builder
After some additional research and testing I discovered that the error in my RegEx Builder was related to attempting to capture the last number in the pattern (x,xxx,xxx). So I broke it out into 3 captures and then perform some math in the for loop. There is probably a better way but I have not yet figured it out. In addition, thanks to esilks input, I removed the line feeds from the multi line variable and converted it to a regular string. Clearly a better way. Below is the updated function. func GetHTMLTableData3() { let stringData = "<tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 30, 2023</span></td><td class=Py(10px) Pstart(10px)><span>405.40</span></td><td class=Py(10px) Pstart(10px)><span>408.22</span></td><td class=Py(10px) Pstart(10px)><span>405.29</span></td><td class=Py(10px) Pstart(10px)><span>407.28</span></td><td class=Py(10px) Pstart(10px)><span>407.28</span></td><td class=Py(10px) Pstart(10px)><span>5,160,100</span></td></tr><tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 29, 2023</span></td><td class=Py(10px) Pstart(10px)><span>400.60</span></td><td class=Py(10px) Pstart(10px)><span>402.67</span></td><td class=Py(10px) Pstart(10px)><span>400.19</span></td><td class=Py(10px) Pstart(10px)><span>402.51</span></td><td class=Py(10px) Pstart(10px)><span>402.51</span></td><td class=Py(10px) Pstart(10px)><span>3,914,800</span></td></tr><tr class=BdT Bdc($seperatorColor) Ta(end) Fz(s) Whs(nw)><td class=Py(10px) Ta(start) Pend(10px)><span>Jun 28, 2023</span></td><td class=Py(10px) Pstart(10px)><span>401.35</span></td><td class=Py(10px) Pstart(10px)><span>403.49</span></td><td class=Py(10px) Pstart(10px)><span>400.71</span></td><td class=Py(10px) Pstart(10px)><span>402.55</span></td><td class=Py(10px) Pstart(10px)><span>400.97</span></td><td class=Py(10px) Pstart(10px)><span>4,320,700</span></td></tr>" let tradingDayPattern = Regex { "<td class=Py(10px) Ta(start) Pend(10px)><span>" Capture(.date(format: "\(month: .abbreviated) \(day: .twoDigits), \(year: .extended(minimumLength: 4))", locale: Locale(identifier: "en_US_POSIX") , timeZone: .gmt)) "</span></td><td class=Py(10px) Pstart(10px)><span>" TryCapture { OneOrMore(.digit) "." Repeat(.digit, count: 2) } transform: { Double($0) } "</span></td><td class=Py(10px) Pstart(10px)><span>" TryCapture { OneOrMore(.digit) "." Repeat(.digit, count: 2) } transform: { Double($0) } "</span></td><td class=Py(10px) Pstart(10px)><span>" TryCapture { OneOrMore(.digit) "." Repeat(.digit, count: 2) } transform: { Double($0) } "</span></td><td class=Py(10px) Pstart(10px)><span>" TryCapture { OneOrMore(.digit) "." Repeat(.digit, count: 2) } transform: { Double($0) } "</span></td><td class=Py(10px) Pstart(10px)><span>" TryCapture { OneOrMore(.digit) "." Repeat(.digit, count: 2) } transform: { Double($0) } "</span></td><td class=Py(10px) Pstart(10px)><span>" Capture { Repeat(.digit, count: 1) }transform: { Int($0) } "," Capture { Repeat(.digit, count: 3) }transform: { Int($0) } "," Capture { Repeat(.digit, count: 3) } transform: { Int($0) } "</span>" } for match in stringData.matches(of: tradingDayPattern) { let (line, date, open, high, low, close, adjClose, v1, v2, v3) = match.output let vol1 = v1! * 1000000 let vol2 = v2! * 1000 let volume = vol1 + vol2 + v3! print("\(date) - \(close) - \(volume)") } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’23
Reply to CoreData Failure During Entity Creation in the ViewContext
I updated my code but used persistentContainer.newBackgroundContext(). Also got rid of the Task and async. The code runs without failure and is very fast. Thank you again for the assistance. Oh, and I learned that I do not have to save twice. class UpdateCoreData: ObservableObject { let persistentContainer = CoreDataStack.shared.persistentContainer @Published var loadingData: Bool = true @Published var updateStatus: String = "" init() { let bgContext = persistentContainer.newBackgroundContext() bgContext.performAndWait { do { var latestDate: Date = Date() var decodedJSON: TradingDays = TradingDays.init(tradingday: []) latestDate = LatestCoreDataDate(fundName: "Fund1", moc: bgContext) decodedJSON = DecodeJSONFile(fileName: "Fund1") AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund1", latestDate: latestDate, moc: bgContext) latestDate = Date() decodedJSON = TradingDays.init(tradingday: []) latestDate = LatestCoreDataDate(fundName: "Fund2", moc: bgContext) decodedJSON = DecodeJSONFile(fileName: "Fund2") AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund2", latestDate: latestDate, moc: bgContext) latestDate = Date() decodedJSON = TradingDays.init(tradingday: []) latestDate = LatestCoreDataDate(fundName: "Fund3", moc: bgContext) decodedJSON = DecodeJSONFile(fileName: "Fund3") AddRecordsToCoreData(jsonData: decodedJSON, fundName: "Fund3", latestDate: latestDate, moc: bgContext) try bgContext.save() } catch let error { print("error in background context = \(error)") } } // end perform and wait persistentContainer.viewContext.vacuum() let persistentStore = persistentContainer.persistentStoreCoordinator.persistentStores.first do { try persistentContainer.persistentStoreCoordinator.remove(persistentStore!) } catch { print("Unable to remove store -> \(error)") } DispatchQueue.main.async { self.loadingData = false self.updateStatus = "Core Date is Updated" } } } func LatestCoreDataDate(fundName: String, moc: NSManagedObjectContext) -> Date { var coreDataValues: [TradingDayClose] = [] var latestDate: Date = Date() let fetchRequest: NSFetchRequest<TradingDayClose> = TradingDayClose.fetchRequest() fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timeStamp", ascending: false)] fetchRequest.predicate = NSPredicate(format: "fundName = %@", fundName) fetchRequest.fetchLimit = 1 do { coreDataValues = try moc.fetch(fetchRequest) } catch let error { print("Error fetching max date. \(error.localizedDescription)") } if coreDataValues.isEmpty { latestDate = Calendar.current.date(byAdding: DateComponents(year: -6), to: latestDate)! } else { latestDate = coreDataValues[0].timeStamp! } return latestDate } func AddRecordsToCoreData(jsonData: TradingDays, fundName: String, latestDate: Date, moc: NSManagedObjectContext) { print("\(fundName)") for item in jsonData.tradingday { if item.timeStamp > latestDate { let newRecord = TradingDayClose(context: moc) newRecord.fundName = fundName newRecord.id = UUID() newRecord.timeStamp = item.timeStamp newRecord.close = (item.close) as NSDecimalNumber } else { break } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jul ’23
Reply to SQLite - Select statement to extract 1 year of data
Not a perfect solution but I use the SQLite function date and now to get the current days date and subtract one year from that. The ideal solution would for Saturdays and Sundays to always return as the preceding Friday. Will work on that and If I can determine a solution will post it. let queryTradingDaysStatement = """ Select FundName, TimeStamp, Close FROM TradingDays WHERE FundName = '\(fundName)' AND TimeStamp >= date('now', 'start of day', '-1 year') ORDER By TimeStamp ASC ; """
Topic: Programming Languages SubTopic: Swift Tags:
Jul ’23
Reply to SQLite - Select statement to extract 1 year of data
Turns out that SQLite supports a case statement. So I incorporated that and strftime into my query to adjust the date back to Friday if the current date is Saturday '6' or Sunday '0'. Works. let queryTradingDaysStatement = """ Select FundName, TimeStamp, Close FROM TradingDays WHERE FundName = '\(fundName)' AND CASE strftime('%w') WHEN '0' then TimeStamp >= date('now', '-2 days', 'start of day', '-\(numYears) year') WHEN '6' then TimeStamp >= date('now', '-1 days','start of day', '-\(numYears) year') ELSE TimeStamp >= date('now', 'start of day', '-\(numYears) year') END ORDER By TimeStamp ASC ; """
Topic: Programming Languages SubTopic: Swift Tags:
Jul ’23
Reply to SQLite - Select statement to extract 1 year of data
Your subquery works but using your suggestion of a With clause appears to me to be the perfect solution. It allows me to get the latest date of available data for a given fund and calculate one year earlier which was my goal. Even better, it is easy to follow. Below is the updated query using a With clause. It works. Thank you. let queryTradingDaysStatement = """ WITH TempTable1 AS ( SELECT max(TimeStamp) as LatestDate FROM TradingDays WHERE FundName = '\(fundName)' ), TempTable2 AS ( SELECT date(LatestDate, 'start of day', '-\(numYears) year') as StartingDate FROM TempTable1 ) SELECT FundName, TimeStamp, Close FROM TradingDays main, TempTable2 temp WHERE main.FundName = '\(fundName)' AND main.TimeStamp >= temp.StartingDate ORDER BY main.TimeStamp ASC ; """
Topic: Programming Languages SubTopic: Swift Tags:
Jul ’23
Reply to Convert SQLite Julian date to a specific date string format for display
Great solution. It makes the query simpler and as you indicated, only requires some simple math to convert between Julian Days and Apples seconds. And yes you are absolutely right that it was a mistake for me to declare a date formatter in a loop. Below is the updated code. func AccessSQLiteData(db: OpaquePointer?) { let queryTradingDaysStatement = """ WITH TempTable1 AS ( SELECT max(TimeStamp) - 365.25 as StartingDate FROM TradingDays WHERE FundName = 'Fund1' ) SELECT main.FundName, main.TimeStamp, main.Close FROM TradingDays as main, TempTable1 as temp WHERE main.FundName = 'Fund1' AND main.TimeStamp >= temp.StartingDate ORDER By main.TimeStamp ASC ; """ let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeZone = .gmt let daysBetween4713And2001: Double = 2451910.500000 let secondsPerDay: Double = 86400.00 var queryTradingDaysCPtr: OpaquePointer? if sqlite3_prepare_v2(db, queryTradingDaysStatement, -1, &queryTradingDaysCPtr, nil) == SQLITE_OK { while (sqlite3_step(queryTradingDaysCPtr) == SQLITE_ROW) { let fundName = sqlite3_column_text(queryTradingDaysCPtr, 0) let daysSince4713BC = sqlite3_column_double(queryTradingDaysCPtr, 1) let close = sqlite3_column_double(queryTradingDaysCPtr, 2) let fundNameAsString = String(cString: fundName!) let daysSinceJanOne2001 = daysSince4713BC - daysBetween4713And2001 let secondsSinceJanOne2001 = daysSinceJanOne2001 * secondsPerDay let timeStamp = Date(timeIntervalSinceReferenceDate: secondsSinceJanOne2001) let formattedTimeStamp = dateFormatter.string(from: timeStamp) let closeAsString = String(format: "$%.2f", close) print(fundNameAsString + " - " + formattedTimeStamp + " - " + closeAsString) } // end while loop } else { let errorMessage = String(cString: sqlite3_errmsg(db)) print("\nQuery is not prepared \(errorMessage)") } sqlite3_finalize(queryTradingDaysCPtr) }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’23
Reply to Updating displayed data generates reentrant warning
It appears that the problem is related to displaying the table before the variable it is based on, fundData, is updated / populated. I solved the problem as follows. 1) I moved the initial sql query from the task tied to Progress View in the main view to the view models init (I could have probably just added isLoading = true to the ProgressView task). This along with the isLoading variable assure that initially fundData is populated with data before the table is displayed. 2) I added isLoading = true to the button action. This once again assures that that fundData is updated before the table is displayed (fundData and isLoading are updated on the main actor at the end of the view models function). Along with these two changes I pulled QueryDatabase into the view model but it is not part of the solution. It just seemed cleaner. Oh, and I made QueryDatabase (function in view model) async since I changed from Dispatch.main.async to MainActor(run:. I do not think this is part of the solution. Below is the updated main view and view model. The button works and it is very fast. // main view struct ContentView: View { @ObservedObject var vm: SQLiteViewModel = SQLiteViewModel() var body: some View { if vm.isLoading == true { ProgressView() } else { HStack { Spacer() .frame(width: 135, height: 200, alignment: .center) Table(vm.fundData) { TableColumn("Fund") { record in Text(record.fundName) .frame(width: 60, height: 15, alignment: .center) } .width(60) TableColumn("Date") { record in Text(sqlDateFormatter.string(from: record.timeStamp)) .frame(width: 120, height: 15, alignment: .center) } .width(120) TableColumn("Close") { record in Text(String(format: "$%.2f", record.close)) .frame(width: 65, height: 15, alignment: .trailing) } .width(65) } // end table .font(.system(size: 14, weight: .regular, design: .monospaced)) .frame(width: 330, height: 565, alignment: .center) Spacer() .frame(width: 30, height: 30, alignment: .center) VStack { Button(action: { Task { vm.isLoading = true await vm.QueryDatabase(fundName: "Fund1", numYears: 1, sortOrder: "main.TimeStamp DESC") } }) { Text("Date Descending") } // end button .frame(width: 140) } // end v stack .font(.system(size: 12, weight: .regular, design: .monospaced)) } // end horizontal stack .frame(width: 600, height: 600, alignment: .center) } } // end view } // view model class SQLiteViewModel: ObservableObject { var db: OpaquePointer? @Published var fundData: [TradingDay] = [] @Published var isLoading: Bool = true var tempTradingDays: [TradingDay] = [] init() { db = OpenDatabase() Task { await QueryDatabase(fundName: "Fund1", numYears: 1, sortOrder: "main.TimeStamp ASC") } } func QueryDatabase(fundName: String, numYears: Int, sortOrder: String) async { tempTradingDays = [] let daysBetween4713And2001: Double = 2451910.500000 let secondsPerDay: Double = 86400.00 var queryTradingDaysCPtr: OpaquePointer? sqlite3_exec(db, SQLStmts.beginTransaction, nil, nil, nil); if sqlite3_prepare_v2(db, SQLStmts.QueryTradingDays(fundName: fundName, numYears: numYears, sortOrder: sortOrder), -1, &queryTradingDaysCPtr, nil) == SQLITE_OK { while (sqlite3_step(queryTradingDaysCPtr) == SQLITE_ROW) { let fundName = sqlite3_column_text(queryTradingDaysCPtr, 0) let daysSince4713BC = sqlite3_column_double(queryTradingDaysCPtr, 1) let close = sqlite3_column_double(queryTradingDaysCPtr, 2) let fundNameAsString = String(cString: fundName!) let daysSinceJanOne2001 = daysSince4713BC - daysBetween4713And2001 let secondsSinceJanOne2001 = daysSinceJanOne2001 * secondsPerDay let timeStamp = Date(timeIntervalSinceReferenceDate: secondsSinceJanOne2001) var tempTradingDay: TradingDay = TradingDay(fundName: "", timeStamp: Date(), close: 0.0) tempTradingDay.fundName = fundNameAsString tempTradingDay.timeStamp = timeStamp tempTradingDay.close = close tempTradingDays.append(tempTradingDay) } // end while loop } else { let errorMessage = String(cString: sqlite3_errmsg(db)) print("\nQuery is not prepared \(errorMessage)") } sqlite3_finalize(queryTradingDaysCPtr) sqlite3_exec(db, SQLStmts.commitTransaction, nil, nil, nil); await MainActor.run(body: { self.fundData = self.tempTradingDays self.isLoading = false }) } }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’23
Reply to NSTableView - How do I remove the horizontal gap between table cells
After some additional review of the NSTableView class, I discovered it contains a var "intercellSpacing" of type NSSize. So I added the line of code below to the "makeNSView" function above and the cell spacing problem is solved. It did not though get rid of the green block to the left of "Stock" or to the right of "Closing Value". If I am able to work that out I will add the solution to this post. tableView.intercellSpacing = NSSize(width: 0.0, height: 0.0)
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’23
Reply to How do I modify height of tableview header cell background color
The solution is to create a subclass of NSTableHeaderCell. In the subclass you override several functions and insert customizations as desired in the drawInterior function. Below is the call to and subclass created. // in func makeNSView let customHeaderCell0 = CustomHeaderCell() customHeaderCell0.stringValue = "Stock" let column0Header = tableView.tableColumns[0] column0Header.minWidth = 90.0 column0Header.headerCell = customHeaderCell0 // subclass final class CustomHeaderCell: NSTableHeaderCell { override init(textCell: String) { super.init(textCell: textCell) } required init(coder: NSCoder) { fatalError("init(coder:) not implemented") } override func draw(withFrame cellFrame: NSRect, in controlView: NSView) { self.drawInterior(withFrame: cellFrame, in: controlView) } override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) { let rect = NSRect(x: cellFrame.origin.x, y: cellFrame.origin.y, width: cellFrame.size.width, height: cellFrame.size.height) NSColor.systemMint.set() NSBezierPath(rect: rect).fill() let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .center let headerCellText = NSAttributedString(string: stringValue, attributes: [NSAttributedString.Key.foregroundColor: NSColor.black, NSAttributedString.Key.font: NSFont(name: "Arial", size: 18)!, NSAttributedString.Key.paragraphStyle: paragraphStyle, NSAttributedString.Key.baselineOffset: -6.0 ]) headerCellText.draw(in: cellFrame) } }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’23
Reply to When creating an instance of a custom NSTableCellView how do pass in a value
The problem appears to be that once I create an instance of CustomTableCellView, the variable nsRectangle is assigned a value based on the current rectWidth value in the override init. And of course your can only override a pre-existing init, none of which include rectWidth. So changing the value of rectWidth after the instance has been created appears to be of no value since the init is only run once. My solution was to pull all of the logic out of the CustomTableCellView init and place it into a function in CustomTableCellView. Then I can create an instance of CustomTableCellView in my Coordinators delegate, which only runs a call to super.init(). On the following line I call the function, named Setup(), to which I pass in rectWidth. The side benefit is this allowed me to simplify the Coordinators delegate code. Below are the updated delegate and CustomTableCellView code. Claude31, thank you for your response. func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let dataCell = CustomTableCellView() if (tableColumn?.identifier)!.rawValue == "fund" { dataCell.Setup(rectWidth: 100.0) dataCell.textField?.stringValue = closingValues[row].fundName } else if (tableColumn?.identifier)!.rawValue == "date" { dataCell.Setup(rectWidth: 120.0) dataCell.textField?.stringValue = SQLDateFormatter.string(from: closingValues[row].timeStamp) } else { dataCell.Setup(rectWidth: 100.0) dataCell.textField?.stringValue = String(format: "$%.2f", closingValues[row].close) } return dataCell } class CustomTableCellView: NSTableCellView { override init(frame frameRect: NSRect) { super.init(frame: frameRect) } func Setup(rectWidth: CGFloat) { self.autoresizingMask = .width let nsRectangle = NSMakeRect(0, 0, rectWidth, 24) let customTextField: NSTextField = CustomTextField(frame: nsRectangle) self.textField = customTextField self.addSubview(customTextField) } required init?(coder decoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Topic: Programming Languages SubTopic: Swift Tags:
Aug ’23