I have a NSViewController containing a view-based table view. When I initialise the view controller and put its view on screent the table view is scrolled to the bottom. I would prefer instead to have the table view scrolled to the top.
I know the method -scrollRowToVisible: but I can't figure where to call it in order for the table view to first appear scrolled to the top. I tried inserting it at the end of the -loadView method of the NSViewController but obviously it doesn't work as the table view hasn't even loaded its rows yet at that point.
What would it be the best place to place the call to the -scrollRowToVisible: method? Or alternatively, is there a property to make the table view show it's first row instead of the last?
I think this is the way to go..
-- you should call scroll once all the item / rows of a table got loaded/displayed in the tableview
-- Somewhere you would do reloadData, Possibly in the awake from nib, This is not a kind of blocking function, it will call some of the UI Delegate function, like
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)aTableColumn byItem:(id)item
Perhaps you should check here for the last item and there might be some Delegate function in the NSTableViewUIDelegate, which tells you when an Item got loaded/displayed in the Table, there you can check for the last item , it means all the table item got loaded then its safe to call scrollToVisible
It's an old question, but for the record, since I ran into this myself today:
The NSScrollView of your table view starts out scrolled to the bottom, most likely because the scroll view restores its last scroll position after closing the window last time.
This is part of the User Interface Preservation / State Restoration feature.
To avoid that the scroll view restores the scroll position (in fact, anything at all), you can subclass NSScrollView and subclass these:
class NonPreservingScrollView: NSScrollView {
override func encodeRestorableState(with coder: NSCoder) {
// Nothing
}
override func restoreState(with coder: NSCoder) {
// Nothing
}
}
Maybe a bit cleaner than trying to track when reloading data has finished and the scroll view won't move when the window loads...
Related
In tvOS development - By the default the focus of the table view is the 1st item. But, how to focus particular item by default in a Table View.
For Example, By default, I want to focus the nth row of the table view.
Thanks for your help in advance.
To control the focus engine in general you have to override the methods provided by UIFocusEnvironment and/or the related UITableViewDelegate and UICollectionViewDelegate methods. This behaves much differently than controlling something like the selected row/item, in that it's a global system and not specific to a particular view.
In your particular case, it may be sufficient to implement the indexPathForPreferredFocusedViewInTableView(_:) method that is available on UITableViewDelegate (note that there is an equivalent method for UICollectionViewDelegate).
One problem I ran into while attempting to do something similar was how the default chain of preferred focus views works. Eventually I noticed this part of the documentation for UIFocusEnvironment (which all views and view controllers conform to):
By default, UIView returns itself and UIViewController returns its
root view. Returning self in a focusable view indicates that view
should be focused. Returning self in an unfocusable view causes the
focus engine to pick a default preferred focused view, by finding the
closest focusable subview to the top-leading corner of the screen.
Returning nil indicates that there is no preferred focused view.
In my case this meant that the focus engine was picking a default preferred focus view and the focus related delegate methods for my UICollectionView were not being called as I expected. If you find this is the case you may need to implement preferredFocusedView (defined in the UIFocusEnvironment protocol) on your view controller subclass and return your instance of UITableView so that the delegate methods related to focus get invoked reliably.
With TVML, it's the attribute autoHighlight="true"
(https://developer.apple.com/library/tvos/documentation/LanguagesUtilities/Conceptual/ATV_Template_Guide/TVJSAttributes.html)
Doesn't work with all templates, though, there still seem to be some bugs in the system.
First thing first,
During the view Load, set:
override var preferredFocusEnvironments: [UIFocusEnvironment] {
return [myTableView]
}
To focus to a particular IndexPath,
func indexPathForPreferredFocusedView(in tableView: UITableView) -> IndexPath? {
if focusAtIndexPathRow != -1 {
return IndexPath(item: focusAtIndexPathRow, section: 0)
} else {
return IndexPath(item: 0, section: 0)
}
}
I have a UITableView that I want to have respond to taps and vertical swipes, but still have something like userInteractionEnabled = NO for horizontal swipes. By that I mean, it would not handle touches and pass the touch event back to its superview.
Things I've tried that didn't work:
returning NO in - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
Overriding touchesBegan/touchesMoved/touchesEnded and passing the event to the next responder
Adding gesture recognizers for horizontal swipes and setting cancelsTouchesInView to YES
I've been trying to fix this on and off for several weeks, so any help is greatly appreciated!
better you can subview empty view for tableview.view color should be
empty and add gesture to that view.if gesture direction is
vertical.scroll tableview
Did you try just adding a horizontal swipe gesture recognizer to your table view's parent view? Also turn off horizontal scrolling on your table view. (set alwaysBounceHorizontal=NO)
Disabling user's interaction for each table view cell should help.
I suggest doing it in cellForRowAtIndexPath method.
The answer to this question is not simple, because UITableView manages a lot of touches, and this touches are managed by different components.
So, to give you a correct and working answer, you have to explain:
what kind of swipes you want to disable (left-to-right, right-to-left)
where (on the empty table view, on the cell)
what happens now when you do this swipe (the cell goes in edit mode, a navigation controller goes to the previous page ecc....)
For example: the swipe-to-delete on a UITableViewCell can't be avoided by overriding touchesBegan:withEvent, because the touch is received by the internal content view (UITableViewCellContentView private class, so you can't subclass and override touchesBegan:withEvent).
But this is not necessary, because you can disable this behavior adding this method to UITableViewController:
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
When you disable the editing (for example) the cell and the table view are no more capable of managing the touch, so the touch is forwarded to the next responder in the chain.
This is only one example, you must explain all the cases that are giving you undesider behavior to let us help you more.
Last suggestions:
At this Page you will find a very good and interesting explanation on how the hit test end event handling process works.
I'll update the answer with more specific information when you'll add more details.
Regards
Fabio
In my iPad application, I am inserting some value on a label. That value should also appear on a UITableView in another class. When I insert the value in the label and navigate to another view, there is no value appearing on the table view but when I scroll the table view up and as it comes to its original position, the value appears. How can I fix it?
Thanks and regards
PC
When you scroll the table you are eventually reloading the data as things become visible. If you call [tableView reloadData] on viewillAppear this should get the label refreshed and displaying correctly for you.
Good Luck!
Check out the reloadData method in the documentation. You will need to call this method after changing the value so the table knows to reload the cells currently being displayed. As you scroll up the cells are being redrawn, which is why you see the new value after a scroll.
Just to clarify, the reloadData method will need to be called on the UITableView object.
[myTableView reloadData];
From the documentation:
Reloads the rows and sections of the receiver.
- (void)reloadData
Discussion
Call this method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on. For efficiency, the table view redisplays only those rows that are visible. It adjusts offsets if the table shrinks as a result of the reload. The table view's delegate or data source calls this method when it wants the table view to completely reload its data. It should not be called in the methods that insert or delete rows, especially within an animation block implemented with calls to beginUpdates and endUpdates
I have a situation whereby I am adding a view from another viewcontroller to an existing viewcontroller. For example:
//set up loading page
self.myLoadingPage = [[LoadingPageViewController alloc]init ];
self.myLoadingPage.view.frame = self.view.bounds;
self.myLoadingPage.view.hidden = YES;
[self.view addSubview:self.myLoadingPage.view];
Is it possible to set 'self.myLoadingPage' to be the first responder? This is the case whereby the loadingpage view size does not cover the entire size of the existing view and users can still interact with the superview (which is not the desired behaviour). I want to just enable the subview in this case.
When I had a similar problem, I made an invisible UIView that covered the entire screen, I added the large invisible UIView on top of the main view and made the loading view a subview of the invisible UIView.
The simplest solution is to override hitTest method in your loading view to return TRUE. This top view is first in the responder chain, the hitTest method gets called which NORMALLY returns TRUE if the point is within the view and will therefore be handled, returning TRUE regardless means you get the touch event and effectively block the message being resent to the next responder.
Interesting question. I found a similar post with a quote from the Apple Developer Forums on this issue:
To truly make this view the only thing
on screen that can receive touches
you'd need to either add another view
over top of everything else to catch
the rest of the touches, or subclass a
view somewhere in your hierarchy (or
your UIWindow itself) and override
hitTest:withEvent: to always return
your text view when it's visible, or
to return nil for touches not in your
text view.
This would seem to indicate there isn't a terribly straightforward solution (unless there was an API change regarding this made after October, 2010.)
Alternatively, I suppose you could go through all the other subviews in your superview and individually set their userInteractionEnabled properties to NO (but that would probably prove more cumbersome than the quoted solutions).
I would love to see other ways to allow this.
I've implemented - (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row in my NSTableView's delegate to resize the height of my table's rows as the width of the leftmost column changes. The problem is that only that column redraws during the resizing (and any column that slide into view during the resize).
This results in the funky visual below after resizing. What I'd like is a way to tell the table view to completely redraw while the user is resizing a column. Right now the most I've been able to do is call setNeedsDisplay after that column finishes resizing.
Check out the NSTableView method noteHeightOfRowsWithIndexesChanged:. You'll need to make an NSIndexSet of the rows whose heights have changed.
update:
In order to have a safe place to call this, you can subclass NSTableColumn to override the setWidth: method. You can then post a custom notification that your table delegate can observe, or you can override the table view also and have the column tell the table to tell its delegate directly.
It may not be the only way, but giving the table view a Core Animation layer fixed it. It's not 100% smooth, but the average user would probably never notice.