TTTableViewController scrollsToTop not working - iphone

I'm using a basic Three20 TTTableViewController subclass which employs its own datasource and model.
The problem is that I cannot seem to use the scrollsToTop property of the table. This is a standard property for the table, inherited from UIScrollView and very commonly used.
I have tried all of the following, in numerous different locations/methods within my class:
self.tableView.scrollsToTop = YES;
[self.tableView setScrollsToTop:YES]
I have also tried overriding the method
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
return YES;
}
All without success.
NB. To be clear, I am referring to the standard gesture of tapping on the status bar to scroll a visible table view to the top (i.e. first row).
Any help much appreciated!

There is some confusion
According to Apple documentation of UIScrollView :
scrollsToTop
A Boolean value that controls whether the scroll-to-top gesture is effective
This is just to tell scrollview that taping status bar will make view scroll to top.
What you are looking for is this UITableView method :
scrollToRowAtIndexPath:atScrollPosition:animated:
Scrolls the receiver until a row identified by index path is at a particular location on the screen.
With three20
Since you are using three20 what you want to do can be easily done thanks to its UITableView+Additions.h > - (void)scrollToFirstRow:(BOOL)animated
[self.tableView scrollToFirstRow:YES];
Without three20
If not using three20, here is how three20 does:
- (void)scrollToFirstRow:(BOOL)animated {
if ([self numberOfSections] > 0 && [self numberOfRowsInSection:0] > 0) {
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop
animated:NO];
}
}
Another way to do this
I think that would also work (untested), using UIScrollView directly, (extracted from three20 too):
- (void)scrollToTop:(BOOL)animated {
[self setContentOffset:CGPointMake(0,0) animated:animated];
}
or :
[self.tableView setContentOffset:CGPointMake(0,0) animated:YES];

Thanks to Vincent G for pointing me in the right direction. It appears that the issue is to do with what code is called in the class's init and viewDidLoad methods.
I discovered that under the following conditions, two tableviews are added as subviews of the view controller's view:
Making reference to the property tableView in the viewDidLoad method, e.g. [[self tableView] setTableHeaderView:]
Building the dataSource of the table within the init / initWithStyle method.
With two UIScrollViews present, the scroll-to-top action does not work properly, as Vincent pointed out. I will file these findings as a bug with the Three20 guys but by following these steps it can at least be avoided for now.
Edit: It seems that this maybe due to the viewDidLoad method being called BEFORE the init in some cases. If reference is made to the tableview in the viewDidLoad, before it has been created, I think the class is creating one. The init will then make another one.

try this:
self.tableview.scrollingEnabled=YES;
self.tableview.scrollsToTop=YES;
Also check that your delegate is returning YES in this method:
scrollViewWillScrollToTop:

As #imnk suggested, my thoughts as an answer, even though I don't have a real solution.
What I'm experiencing (and just verified to be sure) is, that once I change the frame property of the tableView, the touch on the statusBar doesn't work anymore...
According to the Apples documentation on UIScrollViewDelegate, every scrollView's delegate (regardless of its size) can return YES on - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView.
But I looked through three20 and changed it in my implemetations of delegates, but with no success.

Related

Best approach to add Static-TableView-Cells to a UIViewcontroller?

I want to add a tableview-look-a-like-login to my app, but it seems to be not that easy to implement. I tried to accomplish my goal using more then one approach, but i am not sure about which solution is the best.
For example, Dropbox and Facebook have a login page like this.
Here are my 3 approaches :
I added 2 UITextfields to my View (no border) and placed a . png behind, which looks like a tableviewcell. ( Not the best approach cause i want to use real tableviews )
I added a Container View to my ViewController placed a tableview with static Table Views inside. The Problem here is, that i dont know how to access the information inside my viewcontroller?
I added a tableview to my ViewController and used dynamic cells with it. Connected the outlets for delegate and datasource to my viewcontroller and initialized them with the delegate and datasource methods. The Problem here is, that i can not use static table views inside a uiviewcontroller.
Is there any better way of solving this problem ?
I would really like to know how to do this in a more elegant way.
EDIT:
A ContainerViewController basically solved this issue for me some month ago.
After embedding one into the main controller you can access it through the prepareForSegue function and define a protocol-based interface for that specific controller to interact with the embedded controller.
If you want to use static cells inside a regular UIViewController, just add the static cells and design them the way you like in interface builder, then connect the table cells as strong IB outlets (weak won't work, make sure they are strongly referenced). This will work flawlessly if you have a few table cells. Then set the view controller as the data source of the tablet view, implement -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section to return the number of cells and implement -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath to return your strongly referenced cell instance for the specified index path. I've used this method for a simple table view in my view controller that had four cells and it is working perfectly. For a large-dynamic data set, I definitely do not recommend this approach but for small, static tables, this does the job right.
I have an idea how to solve this. I think it's a clean way to do so. You do not need storyboard for this controller.
Make your controller subclass UITableViewController like so:
#interface YourViewController : UITableViewController
Then in your viewDidLoad you create the instances of the cells:
- (void) viewDidLoad {
usernameCell = [YourTextFieldCell new];
passwordCell = [YourTextFieldCell new];
}
The YourTextFieldCell is of course your own subclass of a UITableViewCell, which could be something like this:
#implementation YourTextFieldCell {
UITextField textField;
}
- (id) init {
self = [super init];
if (self) {
// Adjust the text's frame field to your liking
textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 200, 20)];
[self addSubview:textField];
}
}
// A getter method to access the textfield from the outside
- (UITextField *) textField {
return textField;
}
#end
Back in YourViewController:
- (NSInteger) tableView:(UITableView *) tv numberOfRowsInSection:(NSInteger) section {
return 2;
}
- (UITableViewCell *) tableView:(UITableView *) tv cellForRowAtIndexPath:(NSIndexPath *) indexPath {
if (indexPath.row == 0) {
return usernameCell;
} else if (indexPath.row == 1) {
return passwordCell;
}
return nil;
}
Do you get where I am going with this? This is how I think you should do it! Good luck!
I think your approach 2 is the best. If you need to access information in the table view controller, from your UIViewController (which will be the parent view controller), you can get a reference to that table view controller with self.childViewControllers.lastObject. In the viewDidLoad method of the UIViewController subclass, you could set yourself as the delegate of the table view with this line if you want:
[[(UITableViewController *)self.childViewControllers.lastObject tableView] setDelegate:self];
That way, you could implement the tableView:didSelectRowAtIndexPath: method in the view controller, which will get the information I'm guessing you need.
If you go with your option 2) using a storyboard and have a ContainerView containing your own subclass of UITableViewController with static cells then you can implement the prepareForSegue: method in your parent ViewController to take a reference to the UITableViewController (it'll be the destinationController of the segue) and also to pass itself down to the UITableViewController subclass if necessary (which should hold onto it with a weak reference).
Disclaimer - This answer will work for any size of UITableView, but if you're just making a login view, Tom's answer will work quite well.
I'm not sure if this will help, but what I did for this was create my own UITableView-esque subclass with a UITableViewCell-esque subclass as well.
This may not be what you want to hear, but I find what I made to be really helpful, since I've used it a number of times now. Basically, you have a UIView with the stylistic approach for the different types (10.0f - 20.0f cornerRadius and a 1px border (divide by UIScreen's scale property for retina). As for the cell, you'll want to have a full sized UIButton on it that responds to your table view for the touch events either with a delegate or by setting the target and tag inside your table view's class.
Last, you'll have a delegate system just like the UITableView for your information for building the specific tables.
In short, you'll need:
2 UIView subclasses (TableView and TableViewCell)
2 Delegates/Protocols (TableViewDataSource and TableViewDelegate)
Optionally
1 Delegate (TableViewCellResponseDelegate)
1 NSObject Subclass (Contains all of the information needed in each cell - Ease of use)
I found Can's solution to be the best / easiest, but unfortunately it breaks in XCode 5.1 --
I found a workaround which builds off the same basic idea, but unfortunately requires a little more involvement: http://www.codebestowed.com/ios-static-tableview-in-uiviewcontroller/
To summarize, you can add TableViewCells directly to views (and create IBOutlets from them, etc), but in order for them to get "moved" to the TableView properly, you need to remove them from the view in code, and you also need to set Auto-Layout constraints in IB.

iphone - scrollRectToVisible issue

This functions perfectly, but I want to make it an once-function, not fixed-function. When I change tableview with other data, the data displays at the index from previous tableview. So my solution to this is implementing the code below. When I implement this, it works, but when I scroll down, it scrolls up all the time, so it is virtually impossible to scroll down further. Any idea how to make it performs only once when I change tableview?
The code:
[tableView scrollRectToVisible:CGRectMake(0,0,1,1) animated:NO];
Edit 21 august:
Guys, thank you very much! The code was in cellforrowatindexpath. I moved it to inside the function which changes tableview and it works like a charm :D
You could override the reloadData method if that is how you are reloading the Table View with new data and put the code in there. Something like this in your table view controller should suffice:
- (void)reloadData {
[super reloadData];
[tableView scrollRectToVisible:CGRectMake(0,0,1,1) animated:NO];
}
If it's scrolling up every time you scroll down, I assume you put the code in the cellForRowAtIndexPath method which will get called every time you want to present the cell. This is not the correct place to put that code.
It IS a once-function. Most probably, this code of yours is executing again & again. If you have kept this in a function such as cellForRowAtIndexPath:, which is called frequently, that may be the cause of this problem. Where have you put it?
HTH,
Akshay

Open UITableView after Click on Button

I have a Tabbar - Application and i want to show data (NSString) from a NSMutableArray in an UITextfield or better in an UITableView after a button-click.
how can i init and open and fill the UITabelView?
regards
That seems like a basic implementation of a UITableView. The Apple UITableView documentation should cover any questions you have on UITableViews. For your button click, tie the IBAction of the button to call [yourTableView reload]. This forces a load of the table view.
Are your tableView and textField in the same tab where your button is?
Say you have stringArray which is holding all the NSString Objects.
then you can set
textField.text = [stringArray objectAtIndex:indexYouWant];
If you want to add the whole array to tableView, you will need to use that stringArray and implement tableView Methods.
But still I am not clear with your question.
This is an overly broad question. Why not look at some example code and try to figure it out? If you get stuck, come back and post a question and some code showing where you are having a problem.
You need to know the Life Cycle of UIViewController and its UIView
Concentrate on the methods that are responsible for the life cycle of the UIViewControllera:
Creature
Init
initWithNibName:
Creating view
(BOOL) isViewLoaded
LoadView
viewDidLoad
(UIView *) initWithFrame: (CGRect) frame
(UIView *) initWithCoder: (NSCoder *) coder
Processing state change view
viewDidLoad
ViewWillAppear: (BOOL) animated
ViewDidAppear: (BOOL) animated
ViewWillDisappear: (BOOL) animated
ViewDidDisappear: (BOOL) animated
ViewDidUnload
Processing memory warning
DidReceiveMemoryWarning
Destruction
ViewDidUnload
was given
Proceeding from this in one of the initialization methods after clicking on the button, you must fill out the table and call after the reloaddata

iOS - setting outlets on a ViewController being used as a UITableView header

I'm using custom headers for my tableview...
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CustomHeaderController *header = [[CustomHeaderController alloc]
initWithNibName:#"TableHeader" bundle:nil];
header.title.text = #"Test";
return header.view;
}
The title label is never set though. I even tried creating a viewWillAppear method and setting it there, but that didn't work. My outlets are set up too!
Thanks!
SOLUTION: View wasn't load on the return header.view call. Call header.view or add a viewDidLoad method to the class of header to get it to work! Thanks all!
So I'm not sure "title" here means what you think it means. Setting the title on a view controller isn't going to affect what's in the view. If you're trying to set a title on a section, you may just want to use tableView:titleForHeaderInSection. tableView:viewForHeaderInSection is for more complex section headers (like if you wanted to put buttons, or multiple rows of text or something like that). If it matters, you can use both of these methods. I'm not 100% certain on the order they're called, but I'm pretty sure it looks for a viewForHeaderInSection and then if that's nil it goes to titleForHeaderInSection.
The function expects a UIView to be returned, but you appear to be returning a UIViewController. Did you mean to do return header.view?

how does one make a subview firstResponder

i am trying to get a subview to become firstResponder. my understanding is that this is done in its viewDidAppear method, like so:
- (void)viewDidAppear {
[self becomeFirstResponder];
}
while overriding canBecomeFirstResponder to return YES:
- (BOOL)canBecomeFirstResponder {
return YES;
}
however, when i insert the subview in its parent view's viewDidLoad method:
- (void)viewDidLoad {
subViewController = [[SubViewController alloc] init];
[self.view insertSubview: subViewController.view atIndex: 0];
[subViewController viewDidAppear: NO];
[super viewDidLoad];
}
(i call viewDidAppear manually, because it does not get triggered automatically), the subview does not become firstResponder.
why does the subview not become firstResponder? and how can i make it firstResponder?
thanks,
mbotta
btw, this is a rewrite of my original question:
i am trying to build an iphone app where a rootviewcontroller object manages two subviews, one of which should react to a user shaking his iphone.
after some digging, i concluded the subview must be made firstResponder in its view controller's viewDidAppear method. moreover, the canBecomeFirstResponder method should be modified to return YES.
so here's what happens: i instantiate the rootviewcontroller in the app delegate. in its viewDidLoad method, i tell it to addSubView firstViewController. all of this works just beautifully.
however, firstViewController does not react to any shaking. i sprinkled some NSLogs around and found that while we DO tell firstViewController in canBecomeFirstResponder to return YES, and while we DO tell it to [self becomeFirstResponder] in viewDidAppear, in actual fact, it is not the firstResponder.
so, my questions are:
1) does a subview actually need to be firstResponder in order to react to shaking?
a) if not, how does one make a subview react without being firstResponder?
2) how does one make a subview firstResponder?
what makes this interesting is that if i perform the same sequence (canBecomeFirstResponder, [self firstResponder], motionBegan:) in a different project with only one view controller, it all works flawlessly. clearly, i must be hitting a design flaw of my own making.
thanks,
mbotta
Not 100% sure, but this could be your problem. If we could see the offending methods it might be easier.
From the Event Handling Best Practices (emphasis added by me):
If you handle events in a subclass of
UIView, UIViewController, or (in rare
cases) UIResponder,
You should implement all of the event-handling methods (even if it is
a null implementation).
Do not call the superclass implementation of the methods.
If you call the superclass methods the events are probably getting passed along to the nextResponder.
EDIT
The Event Handling Best Practices link above is dead. I couldn't find that pull quote anywhere, but Event Handling Guide for UIKit Apps seems to be the most relevant.