NSScrollview/NSTableView scroll to bottom by default

Hello again!

I am continuing work on my project that I have recently posted about:

here: http://macscripter.net/viewtopic.php?id=40415
and here: http://macscripter.net/viewtopic.php?id=40329 <–more relevant to my current post.

I am working with a NSTableView that is part of an NSScrollView in my interface. I have created the necessary means (per help from StefanK and Shane Stanley) to pull the contents of a text-based log file, through an array controller, and into a single column table. The contents of the log file can range from 20 lines to 2000. What I would like to do is that each time the table is updated with log contents, that it scroll to the bottom so that the most recent lines of the log are displayed. Continuing the code from the (http://macscripter.net/viewtopic.php?id=40329) more relevant link above, I have easily worked to establish the number of lines in any given update from the log file per the following:

set rowNumber to count of lines of textParagraphs as integer

I have then attempted to call various tasks of the NSScrollView and NSTableView classes through documentation reference. (I think I understand how to address the classes and their tasks, I think what is the key point missing from the Xcode docs is all of the necessary parameters. ASOC would be much easier with these, if I am understanding things correctly.)

Using that line above, I have tried to call the following:

NSScrollView’s contentView
NSScrollView’s reflectScrolledClipView:
NSTableView’s moveRowAtIndex:toIndex:
NSTableView’s rowForView:
NSTableView’s scrollColumnToVisible:
NSTableView’s columnAtPoint:

I have a property declared for both the TableView and the ScrollView with missing value, and have verified they are both bound correctly. I am just not catching on to how to call the various method(s) above with my ‘rowNumber’ variable as the last line for the NSScrollView/TableView to sit at by default after it is updated. I have also tried to play with NSScroller to set this value to somewhere in between 0.0 and 1.0 but I am not understanding the CGFloat concept mentioned in the Xcode documentation. Any help would be greatly appreciated. I found an article on possibly how to do this in Obj-C but I am not sure I am implementing the concept correctly in ASOC.

That link is here: http://stackoverflow.com/questions/1799728/how-to-make-nstableview-scroll-to-most-recently-added-row

Hi,

there actually no need to bind also the scroll view.
assuming there’s a property tableView for the NSTableView instance and arrayController for the NSArrayController instance, to update the table and scroll to the bottom write


arrayController's rearrangeObjects()
tableView's scrollRowToVisible_(arrayController's arrangedObjects()'s |count|() - 1)

Stefan,

Thanks again for the help. It’s interesting that this can be done without addressing the scrollview at all, considering scrolling is the desired action. I have double-checked my property for the tableView and my property for the arrayController are correct in their bindings.

I inserted your code as follows: (borrowed from my relevant link above)


set unixpath to "/Library/app/dir/place/loc1/log"
set UTF8StringEncoding to current application's NSUTF8StringEncoding
set txt to current application's NSString's stringWithContentsOfFile_encoding_error_(unixpath, UTF8StringEncoding, missing value)
set newlineCharacterSet to current application's NSCharacterSet's newlineCharacterSet()
set textParagraphs to txt's componentsSeparatedByCharactersInSet_(newlineCharacterSet) -- as list (maybe)
log textParagraphs
set my appLog to textParagraphs
set rowNumber to count of lines of textParagraphs as integer
appLog's rearrangeObjects()
appTable's scrollRowToVisible_(appLog's arrangedObjects()'s |rowNumber|() - 1)

Per that implementation, I receive: -[__NSArrayM rearrangeObjects]: unrecognized selector sent to instance 0x40015c500 ----in the log.


set unixpath to "/Library/app/dir/place/loc1/log"
set UTF8StringEncoding to current application's NSUTF8StringEncoding
set txt to current application's NSString's stringWithContentsOfFile_encoding_error_(unixpath, UTF8StringEncoding, missing value)
set newlineCharacterSet to current application's NSCharacterSet's newlineCharacterSet()
set textParagraphs to txt's componentsSeparatedByCharactersInSet_(newlineCharacterSet) -- as list (maybe)
log textParagraphs
set rowNumber to count of lines of textParagraphs as integer
appLog's rearrangeObjects()
appTable's scrollRowToVisible_(appLog's arrangedObjects()'s |rowNumber|() - 1)
set my appLog to textParagraphs

Per that implementation, I receive no response from the logs, and the refresh of the log fails to complete, and does not pass log lines through the array controller, presumably because it is now the last task in the button’s action. Any ideas? What else can I check to troubleshoot the message from the logs in the first implementation? Thanks again.

the error points out that the property appLog is just an array, not an )array controller.

There are two ways to control the datasource of a table view:

¢ implementing table view delegate methods numberOfRowsInTableView: and objectValueForTableColumn:row:
¢ using NSArrayController with bindings

in the first way you call reloadData of NSTableView to refresh the table, in the second rearrangeObjects of NSArrayController

Sorry for the slow response, and thanks for the insight. Just for documenting for the community, how are you able to tell that the error points this out? Is it the M notation after the class name in the error, OR is it the fact that had this been an array controller property, the error (hypothetical) would show the NSArrayController class name? My guess is that the latter is probably true.

The method I took to resolve based on your response was to create a property declaration for the array controller itself (not sure why I didn’t do this before) and make the appropriate calls in the code. My code now looks as you posted before, but with the appropriate properties called. Thanks again for your assistance.

You can even see it in the code.
In the first script in post #3 the line


set my appLog to textParagraphs

assigns the value of variable textParagraphs to the property myLog, which is an array (NSArray)
as the methods componentsSeparatedBy. return always an array

Just to expand a bit…

Several classes, including NSArray, NSString and NSDictionary, are represented in Cocoa as “class clusters”. That means when you create an instance of one, you don’t get back an NSArray object or whatever, but rather an object of one of several possible subclasses. These are private subclasses, hence the double-underscore at the beginning of the names, and they have the same methods. The names generally make it obvious what class they are a subclass of (eg, _NSArrayM), but you can check using isKindOfClass.

Here’s a simple example:

tell current application's NSString to set x to stringWithString_("one")
log x's class --> __NSCFString
tell current application's NSString to set x to stringWithString_("true")
log x's class --> __NSCFConstantString

The clusters are used to optimize things: the different subclasses can have things like different sorting algorithms, etc, etc.

Just wanted to post my project link for others to view. Doing so on all applicable posts made to create the project. Hopefully someone can benefit in the future…

https://github.com/loyaltyarm/mac/tree/master/Syncafee