I made a NSTableview and a custom tablecellview-class "KSTablecellview" which defines the cells inside the tableview.
Now I'm trying to access these cells and the elements inside them at a certain time, for example triggered through an users input.
For example I wanna change the text of a textfield in one of my custom cellviews. I tried that:
Outlet_TableView.selectRowIndexes(IndexSet(integer: 0), byExtendingSelection: false) // Selecting row #1
let view = Outlet_TableView.view(atColumn: 0, row: 0, makeIfNecessary: false) as! KSTableCellView // getting cellview, at first row/ first column (-> crashes)
view.myTextField.stringvalue = "Hello World"
This fails giving me "[...] [default] Unable to load Info.plist exceptions (eGPUOverrides)"
I tried some other things and tried to work out the problems reason, but I wasn't able to do it, I'm still a beginner.
Any help would be really great.
I'm using XCode and Swift, trying to build an app for Mac OS.
You should try to avoid updating the content of your cells manually. Instead, you should reload the data from your data source.
func reloadData(forRowIndexes rowIndexes: IndexSet,
columnIndexes: IndexSet)
Which will in turn fetch the data from your data source at the given index/column.
For example, your data source would probably contain "Hello World" at the given index after the user event occurred (provided you updated your data source content), and would thus be used in the dataCellFor: delegate method.
May be you're doing these operations before table is loaded for the first time(eg. inviewDidLoad) or on tableView with numberOfRows = 0.
Until tableView loads that row, it won't create the views inside that row and tableView.view(atColumn:, row:, makeIfNecessary: false) will return nil and force unwrapping this nil will make your application crash.
Related
Update: this problem is more focused now, and not on quite the same topic. I've asked this question as a follow-on
ORIGINAL QUESTION:
I am getting a crash on a subclassed WKWebView-provisioned app.
ProcessAssertion::acquireSync Failed to acquire RBS assertion 'ConnectionTerminationWatchdog' for process with PID=87121, error: Error Domain=RBSServiceErrorDomain Code=1 "target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit" UserInfo={NSLocalizedFailureReason=target is not running or doesn't have entitlement com.apple.runningboard.assertions.webkit}
The problem is, that I can't tell if this is related or not. The actual error on crash is
Thread 1: EXC_BAD_ACCESS (code=1, address=0xbdb2dfcf0470)
Which I was assuming was something running off the end of an array. This makes some sense: I'm selecting from a table that filters out some entries from the data source; but I've checked that carefully; there is no point when a row index greater than the actual rows is accessed (and yes, I'm accounting for the difference between count and index).
The main change here is that I previously had a UIView that acted as a container for a number of CAShapeLayers. I also wanted to overlay text view, but with the proviso that this be via a WKWebView. With two separate views, I would have to either have the CAShapeLayer objects in front of, or behind the WebView. I was seeking a fix to that.
What I have done is substitute a WKWebView for the original UIView. I can add the CAShapes to it, so it performs the original function. It also can, presumably, display the html. And the original suggestion in this answer to a question I asked is what I am working towards. The idea being that it would allow the effect sought, with shapes in front of or behind the html elements.
But the error is thrown after the DidSelect call on the table:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
Tracker.track("getting row \(indexPath.row)")
let ptv = tableView as? NovilloTableView
if ptv!.uiType == .textTable {
let gp = Projects.currentProject?.getPaths(type: PaletteView.getCurrentPane())
GitPaths.currentGitPath = gp![indexPath.row]
NotificationCenter.default.post(name: NNames.updateWebText.nn(), object: nil)
return
}
let svgs = Projects.currentProject!.getPaths(type : PaletteView.getCurrentPane())
var gitPath = svgs[indexPath.row]
var gitPaths = GitPaths.getMediaBoundingBoxes(paths: [gitPath])
guard let pathArrays = gitPath.parseForRegBeziers() else { return }
let rslt = pathArrays.0
let regBeziers = pathArrays.1
gitPath.boundingBox = gitPath.getBoundsParamsForPaths(src: regBeziers.isEmpty ? rslt : regBeziers)
GitPaths.currentGitPath = gitPath
// Tracker.track("sending notification")
NotificationCenter.default.post(name: NNames.updateMedia.nn(), object: nil,
userInfo: ["path" : gitPath])
Tracker.track("completed didSelect")
return
}
In other words, the interaction has ended; but I get this crash, even though I can se that the expected result (the rendering of the shapes into the view) has been completed correctly. It seems to be happening right after everything has completed, and no amount of breakpoints has shown anything else to be happening.
This is confusing to me, and I have hit a limit on what I know how to do to dig further into this. Given the nature of web connections, I've wondered if it's some asynchronous issue that I can't debug sequentially; but that's guesswork without any direct evidence.
I suspect that there is a possible configuration problem with the WebView that shows up once I interact with it, by changing its contents. I'm not actually trying to get it to load anything when it crashes, it's only performing its original function as a container for the CAShapeLayers, so I'm confused.
The main view that contains the WKWebView (which is subclassed, to support a function to determine if it should display the web content, and which I've commented out), is set as the delegate for the WKWebView, and that seems to be fine, though there are no actual protocol functions added to that view, not sure if that matters.
The other detail is that the WebView when it does load web content is only loading local text, and not connected to any services. This testing is happening in the Simulator, and I've come across advice elsewhere to allow for background processes that include enabling Background Fetch, etc., but this has done nothing to change the situation...
EDIT: this is the extent of the configuration of the subclassed WKWebView: maybe this is the issue?
mediaDisplay = NovilloWebView()
mediaPane.addSubview(mediaDisplay)
mediaDisplay.navigationDelegate = self
mediaDisplay.uiDelegate = self
mediaDisplay.backgroundColor = .clear
I have a tableview and an api and api call function. I just use my apicall() function on the viewDidLoad() and Table.reloadData() once the data is received . This works fine on the first time when i am getting this screen.
I have a code inside the tableview.
zerointdeparturehour = String(departurehour[indexPath.row])
Which gets a time from an array that gets populated inside the api calling function.
Everything works fine as it should. The view loads , api gets called , gets data and tableview reloads and gets populated.
and if the api gets called again, all the array values created from the api call are removed with departurehour.removeAll(). (I used this so i can call the api again and again within the same screen for different dates)
BUT,
if i press the backbutton (default backbutton) to go back to previous page and if i try to come back to this page again, viewDidLoad() calls the apicall() function,
but i get error at
zerointdeparturehour = String(departurehour[indexPath.row]) - Index out of bounds.
Why does this happen ONLY when i move back from tableview page back to previous page and come back to this tableview page again? ( I know this happens because the departurehour array is nil at the moment because api hasnt filled the information inside it)
Why doesn't happen on the first time.
I get my row from Api as well, which works on the first time.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return flightcount // comes from api
}
I do have a function to check if the flightcount is 0 or not and what to do when count is 0 .
The app was crashing because flightcount kept the value from previous screen and the table tried to load with that value and hence the crash.
I fixed it with simply reinitialising it as 0.
override func viewDidAppear(_ animated: Bool) {
flightcount = 0
}
On the previous screen. Thanks for pointing me in the right direction. (both people who commented)
There are two possible solutions:
Synchronize departurehour and flightcount dataArray, you should not reload tableview until you have both the arrays ready.
Or add a check before accessing departurehour, whether it is empty or not:
if !departurehour.isEmpty {
zerointdeparturehour = String(departurehour[indexPath.row])
}
I'm seeing crashes that either shouldn't be possible, or are very much possible and the documentation just isn't clear enough as to why.
UPDATE:
Although I disagree with the comment below asking me to separate this into multiple SO questions, if someone could focus on this one I think it would help greatly:
When are notifications delivered to the main thread? Is it possible that the results on the main thread are different than they were in a previous runloop without being notified yet of the difference?
If the answer to this question is yes the results could be different than a previous runloop without notifying then I would argue it is CRUCIAL to get this into the documentation somewhere.
Background Writes
First I think it's important to go over what I am already doing for writes. All of my writes are performed through a method that essentially looks like this (error handling aside):
func write(block: #escaping (Realm) -> ()) {
somePrivateBackgroundSerialQueue.async {
autoreleasepool {
let realm = try! Realm()
realm.refresh()
try? realm.write { block(realm) }
}
}
}
Nothing crazy here, pretty well documented on your end.
Notifications and Table Views
The main question I have here is when are notifications delivered to the main thread after being written from a background thread? I have a complex table view (multiple sections, ads every 5th row) backed by realm results. My main data source looks like:
enum StoryRow {
case story(Story) // Story is a RealmSwift.Object subclass
case ad(Int)
}
class StorySection {
let stories: Results<Story>
var numberOfRows: Int {
let count = stories.count
return count + numberOfAds(before: count)
}
func row(at index: Int) -> StoryRow {
if isAdRow(at: index) {
return .ad(index)
} else {
let storyIndex = index - numberOfAds(before: index)
return .story(stories[storyIndex])
}
}
}
var sections: [StorySection]
... sections[indexPath.section].row(at: indexPath.row) ...
Before building my sections array I fetch the realm results and filter them based on the type of stories for the particular screen, sort them so they are in the proper order for their sections, then I build up the sections by passing in results.filter(...date query...) to the section constructor. Finally, I results.observe(...) the main results object (not any of the results passed into the section) and reload the table view when the notification handler is called. I don't bother observing the results in the sections because if any of those results changed then the parent had to change as well and it should trigger a change notification.
The ad slots have callbacks when an ad is filled or not filled and when that happens instead of calling tableView.reloadData() I am doing something like:
guard tableView.indexPathsForVisibleRows?.contains(indexPath) == true else { return }
tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.endUpdates()
The problem is, I very rarely see a crash either around an index being out of bound when accessing the realm results or an invalid table view update.
QUESTIONS
Is it possible the realm changed on the main thread before any notifications were delivered?
Should table view updates other than reloadData() simply not be used anywhere outside of a realm notification block?
Anything else crucial I am missing?
There's nothing in your code snippets or description of what you're doing that jumps out at me as obviously wrong. Having a separate callback mechanism that updates specific slots independent of Realm change notifications has a lot of potential for timing related bugs, but since you're explicitly checking if the indexPath is visible before reloading the row, I would expect that to at worst manifest as reloading the wrong row and not a crash.
The intended behavior is that refreshing the Realm and delivering notifications is an atomicish operation: anything that causes the read version to advance will deliver all notifications before returning. In simple cases, this means that you'll never see the new data without the associated notification firing first. However, there's some caveats to this:
Nested notification delivery doesn't work correctly, so beginning a write transaction from within a notification block can result in a notification being skipped (merely calling refresh() can't cause this, as it's just a no-op within a notification). If you're performing all writes on background threads you shouldn't be hitting this.
If you have multiple notification blocks, then obviously anything which gets invoked from the first one will run before the second notification block gets a chance to do things, and a call to tableView.reloadData() may result in quite a lot of things happening within the notification block. If this is the source of problems, you would hopefully see exceptions being thrown with a stack trace coming from within a notification block.
I am facing an issue while displaying google maps in a table view cell with swift. I want to display check In (Latitude and Longitude) based on this I want to display google map in table view. I will latitude and longitude from server if no location available means I will get null. so, In one scenario I am getting correct, but while reloading the top-level I am getting map when and where their latitude and longitude is null also. Please guide me.
A map view is an expensive view to instantiate. Even when using dequeueReusableCellWithIdentifier it will still be resource-heavy with a large number of items.
I believe using a way to generate a static map image from your longitude/latitude combination is your best bet.
You can take a look here and you can see how to easily construct an API call to many popular map providers such as Google for a static image map like for example:
http://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=13&scale=false&size=600x300&maptype=roadmap&format=png&visual_refresh=true
Also worth mentioning that some of those providers might have limitations (Like Google might need an API key in case of high traffic). So choose wisely after some research.
Looks like you are facing two issues
1) Already mentioned one "Map appears even when data is nil"
2) Performance issue (It is not mentioned here though)
The first one is due to dequeueing of cell. When you reload table , the cell will be reused(Not created again).
When you return your cell for row in
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
the cell with map view may get return, that is why you get unwanted map there. You should make it nil to prevent this.
2:
When came to performance, its a not the right approach to show mapview in every cell. An alternate approach is to make image out of your map url
use this
let mapUrl: String = NSURL(string:"YourMapURL")
let mapImage = UIImage.imageWithData(NSData.dataWithContentsOfURL(mapUrl))
let latitude:Double = 17.3850
let longitude:Double = 78.4867
let imageURL = NSURL(string: "http://maps.googleapis.com/maps/api/staticmap?center=\(latitude),\(longitude)&zoom=12&scale=false&size=600x300&maptype=roadmap&format=png&visual_refresh=true")
let imagedData = NSData(contentsOfURL: imageURL!)!
staticMapImgView.image = UIImage(data: imagedData)
This is a followup question to a question previously asked before.
didselectatrow updating multiple cells below reloaded tableview + counter
So after much reading and experimenting even after I accepted the guys answer I am still having the same issue. I am going to try a much different approach to it after reading around I found use of adding/removing the index path by having a Set <NSIndexPath>() var that I am constantly checking.
After much experimentation I finally got a working check on the Set <NSIndexPath> the problem is for some reason in xcode 7 when I try to create a second array to capture the integer values related to the set xcode freezes up on indexing. Which is altogether a different issue. How do I go about keeping track of not only the indexpath row but also the counter as well? My latest code to insert/remove indexpaths at row is:
if indexCount == 1 || indexCount == 2 {
CountIndex.insert(indexPath)
} else {
CountIndex.remove(indexPath)
}
My latest try was to try creating a dictionary that stores the indexpath + the Counter in a dictionary like so var cardCountindexCount = [NSIndexPath: Int]()
Then setting it in the func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { by cardCountindexCount = [indexPath: cardCount]. However I am lacking any experience on going about this approach as it is all experimental and it seems that what I get when I try to recall this is a similar problem. Instead of actually changing the text label in the didselectatrow I am now trying to change it in cellforrow method and creating a universal variable that can store the data changes.
I am willing to admit I am slightly out of my depths on this problem as I have only ever worked with simple tableviews prior. More guidance would be greatly appreciated!
The way I ended up fixing this was by setting the relationship in core data, then saving the quantity of the card in core data, and finally recalling the object relationship and quantity back into the func tableview willDisplayCell method showing the changes in text in that method.