UIRefreshControl not working properly on tabbed application - uitableview

I have a tabbed application, and on the second tab there is a refreshControl, where the user can pull down on the UITableViewController to refresh the data.
In viewDidLoad I have the following...
UIRefreshControl *myRefreshControl = [[UIRefreshControl alloc] init];
[myRefreshControl addTarget:self action:#selector(refreshData:) forControlEvents:UIControlEventValueChanged];
self.refreshControl = myRefreshControl;
Pull this down as many times as you want, and it will refresh correctly, calling refreshData:.
However, as soon as you go to a different tab, and then come back, it no longer work. refreshData: is never called.
By setting self.refreshControl = myRefreshControl, does it not persist when coming back to this view? (If I create a #property for a UIRefreshControl in my .h, and assign self.refreshControl to that... it doesn't persist either.)
Do I HAVE to put the above code in viewWillAppear intsead? Is there another way to assign a UIRefreshController to self.refreshControl ONCE, and not have to create and assign one to it each time on something like viewWillAppear?

Related

Show UIActivityIndicatorView when switching view controllers

I have a view controller that segues to a second view controller which loads several images but it hangs for a second or two before seguing from the first VC to the second. I am trying to add a UIActivityIndicatorView so that the user doesn't think the app is frozen (which is currently what it feels like). However I can't seem to get it to work properly and all of the examples I've seen are using a web view or are accessing some kind of data from a server whereas I'm loading images that are stored in the app.
I have some code below to show what I have attempted.
.h file
#interface SecondViewController: UIViewController
#property (strong, nonatomic) UIActivityIndicatorView *indicator;
.m file
-(void)viewWillAppear:(BOOL)animated
{
self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.indicator.center = CGPointMake(160, 240);
[self.view addSubview:self.indicator];
//Loading a lot of images in a for loop.
//The images are attached to buttons which the user can press to bring up
//an exploded view in a different controller with additional information
[self.indicator startAnimating];
for{....}
[self.indicator stopAnimating];
}
I have tried also using dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) immediately after the call to [self.indicator startAnimating] but all that happened was that the view controller loaded instantly and the images/buttons never loaded at all.
How can I get rid of the delay when the user clicks the "next" button on the first view controller? The app hangs on the first VC for about a second or two then finally loads the second view controller with all the images/buttons. Do I need to add the UIActivityIndicatorView to the first view controller instead or am I going about this completely the wrong way? I'm open to any and all methods to get this done, thanks in advance.
You need to call the initialization code and stopAnimating in the next run loop. One easy thing you can do is the following:
-(void)viewWillAppear:(BOOL)animated
{
self.indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.indicator.center = CGPointMake(160, 240);
[self.view addSubview:self.indicator];
//Loading a lot of images in a for loop.
//The images are attached to buttons which the user can press to bring up
//an exploded view in a different controller with additional information
[self.indicator startAnimating];
[self performSelector:#selector(loadUI) withObject:nil afterDelay:0.01];
}
-(void) loadUI {
for{....}
[self.indicator stopAnimating];
}
Of course there are other ways to run loadUI in the next run loop (such as using a timer).

removing a view controller on click of a button

I am developing a library for a iOS app that will authenticate the users on the basis of the credential entered by them.
What I am doing is that, from a view controller say TestViewController I am setting a new view controller LoginScreenViewController on top of TestViewController when user taps a button by using the following code.
LoginScreenViewController * LoginScreen = [[LoginScreenViewController alloc] initWithNibName:nil bundle:nil];
[self.view addSubview:LoginScreen.view];
This LoginScreenViewController contains a Cancel button, on which I want to remove the LoginScreenViewController. For this I am using following code
[self.view removeFromSuperview];
the problem is when I am executing this code, my app is crashing.
Since my LoginScreenViewController is defined by the Library, I want it to be independent of the caller's implementation.
EDIT:
In LoginScreenViewController.m I have used following code to add an event handler to the cancel button.
[button addTarget:self action:#selector(CancelButtonHandler:) forControlEvents:UIControlEventTouchUpInside];
The app is crashing when I am tapping the Cancel button. Even if the CancelButtonHandler does not contain any line of code.
You are trying to remove wrong view. To remove LoginScreen view do:
[LoginScreen.view removeFromSuperview];

Header Displaced in TableView with UIRefreshControl

My UIRefreshController is doing something odd.
When I pull-down refresh, the tableView headers are displaced.
If I pull-down it looks fine, but if I scroll down the table while the refresher is still working, the headers are offset by the height of the refresh control while the UITableCells are fine and scroll behind the header.
I want to avoid creating a tableViewController, and so I am doing the following in viewDidLoad:
_refreshControl = [[UIRefreshControl alloc] init];
[_refreshControl addTarget:self action:#selector(refresh) forControlEvents:UIControlEventValueChanged];
[_tableView addSubview:_refreshControl];
I have a lot of tables in different view controllers that require this functionality. Is there any way I can avoid making a UITableViewController for each one?
Thanks a ton!
This could be an issue due to the fact that you are adding _refreshControl as a subview which is not supposed to be done. However you can create a UITableViewController object add it as the child view controller of your current viewcontroller class.
For eg:-
UITableViewController *tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self addChildViewController:tableViewController];
tableViewController.refreshControl = [[UIRefreshControl alloc] init];
[tableViewController.refreshControl addTarget:self action:#selector(refresh:) forControlEvents:UIControlEventValueChanged];
tableViewController.tableView.frame = CGRectMake(...);//set the frame here
[self.view addSubview:tableViewController.tableView];
a quick fix to this is to go like this
Objective-C
//header
#property UITableViewController *tableController;
//.m (right at the beginning of viewDidLoad for example)
self.tableController = [[UITableViewController alloc] init];
[self addChildViewController:self.tableController];
self.tableController.tableView = self.tableView;
...
//then create the refresh control and assign it to the UITableViewController
self.tableController.refreshControl = refreshControl;
Swift 2.1
//Create an instance of a UITableViewController. This will host your UITableView.
private let tableController = UITableViewController()
//Add tableController as a childViewController and set its tableView property to your UITableView.
self.addChildViewController(self.tableController)
self.tableController.tableView = self.tableView
self.refreshControl.addTarget(self, action: "refreshData:", forControlEvents: .ValueChanged)
self.tableController.refreshControl = self.refreshControl
this helps if you have your table hooked up to an IBOutlet and have other things linked into the storyboard you dont feel like messing with.
UIRefreshControl's aren't meant to be subviews, they're meant to (literally) be the table's refresh control. UITableViewController has an outlet specifically for them (again, literally called refreshControl) that you should be using. As a subview of the table, you may be causing the table to assume it's a cell, rather than just a subview, which forces a recalculation around it. There will be cases where you do get lucky and the control may set itself in the right place, but this is, again, the result of undefined behavior.
UITableViewController is not meant to be a limiting class, and it certainly should not keep you from implementing "multiple table views" (which sound context-specific enough that they'd warrant a new view controller presented anyhow). If you are worried about having to write boilerplate for each class, write an abstract superclass controller for every table view you want to implement, and subclass it as necessary.
#available(iOS 10.0, *)
tableView.refreshControl = refreshControl
Try this way to add push view controller.
Create a table view controller and add it as the sub view of existing view controller. Then assign your table view and refresh controllers to tableview controller's properties.
UITableViewController *newTableViewController = [[UITableViewController alloc] init];
newTableViewControler.tableView = <yourTableView>;
<yourRefreshController> = [[UIRefreshControl alloc] init];
[<yourRefreshController> addTarget:self
action:#selector(refreshTableView:)
forControlEvents:UIControlEventValueChanged];
newTableViewController.refreshControl = _chatListRefreshControl;
[self addChildViewController:newTableViewController];

Using Kal calendar from existing table view and popToRootViewController not working

I'm trying use Kal calendar in my project. Here is what I understand to be the case so far:
Whichever view controller that calls Kal must implement the UITableViewDelegate method "didSelectRowAtPath"
Issue:
My view controller that calls Kal already has an existing tableview (one of the cells would call Kal)
To solve this, I've attempted to push an intermediary view controller from a cell in my main tableview as follows:
ScheduleViewController *svc = [[ScheduleViewController alloc] init];
[svc setTitle:#"Schedule"];
[self.navigationController pushViewController:svc animated:YES];
From ScheduleViewController's viewDidLoad, I'm pushing the actual Kal calendar:
KalViewController *kal = [[KalViewController alloc] init];
[kal setDelegate:self];
[kal setTitle:#"Schedule"];
[self.navigationController pushViewController:kal animated:NO];
So once I do this, I can get the calendar to show up. However, I can't seem to cleanly get back to my main menu using the back button. I created a back button in ScheduleViewController:
[kal.navigationItem
setBackBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(test)]];
I've put this in a few different places (viewDidLoad, viewWillAppear) and can't get it to work. When I click "Back" it still goes to ScheduleViewController instead of all the way back. "test" never gets called. I've tried popToRootViewController, [[self.presentingViewController presentingViewController] dismissViewController], and a few others. Just doesn't want to work.
Is this the best way to go about using Kal?
How can I popToRootViewController (skipping ScheduleViewController) using this method?
Thanks! This is my first post so please let me know if there's anything I can do to make the question less confusing.
Well, finally got it I think. I ended up removing the intermediary view controller. I think I just didn't understand what was going on earlier...I've now done the following:
instead of my main view controller (w/the existing tableview) being the delegate, I made the KalDataSource also be the UITableViewDelegate. So my DataSource handles both populating the calendar and selection of an event.
added id datasource as in ivar in main view controller
changed the "didSelectRowAtPath" method to:
dataSource = [[EventsDataSource alloc] init];
KalViewController *kal = [[KalViewController alloc] init];
[kal setDelegate:dataSource];
[kal setDataSource:dataSource];
[kal setTitle:#"Schedule"];
[self.navigationController pushViewController:kal animated:YES];
Everything now appears to be okay.

strange space issue when adding Kal as subview

There's too much black space between the Kalendar and the navigation bar. Here's a picture of what I'm talking about:
I'm calling the code from ViewDidLoad of a ViewController that I have in my storyboard:
- (void) viewDidLoad {
KalViewController *calendar = [[KalViewController alloc] init];
[self.view addSubview:calendar.view];
}
I'm just learning to use Kal. Maybe I'm going about incorporating it in the wrong way. I sure do like using the storyboard to link up my views so my viewcontroller includes "Kal.h" and I instantiate as shown in the code above. I tried to make the viewcontroller a subclass of KalViewController but that didn't work.
Maybe there's a more elegant way of incorporating Kal into a hierarchy of storyboarded view controllers?
Just add the following:
[[self navigationController] initWithRootViewController:calendar];
[[calendar navigationItem] setHidesBackButton:YES];
and it should do the trick. The second line is being used to hide the "Back" button that otherwise becomes visible, but for me it's a single screen app, you may or may not want to use it.
I used the exact same code as you initially did ACB, had the exact same problem but I also had included [self addChildViewController:calendar]; after the creation of the calendar view.
What does this code do? [self addChildViewController:calendar];
I understand it "adds a child view controller" but what is REALLY going on.... and should I need this? Apparently not because you didn't, and I just compiled my code without it and there was no difference.
Overall code:
KalViewController *calendar = [[KalViewController alloc] init];
[self.view addSubview:calendar.view];
//[self addChildViewController:calendar];

Resources