UITableView Activity Indicator the Apple way - ios

My problem is pretty common. I want to show some "loading" stuff while the data that will be shown in a table is being downloaded. But all the solutions I've seen this far is either to add some spinners to cells, or put some semi-transparent view over the app.
What I want to active is the thing that apple does in its apps (for example YouTube). Like, the spinning wheel with word "Loading" is shown on the white background, and then the data is shown.
I have also seen some solution with a grouped style, but my table has a plain style so it isn't helping me a lot I think.
EDIT
My final code looks like this:
- (void)loadImagesData
{
UIView *tableView = self.view;
self.view = [[LoadingView alloc] initWithFrame:CGRectMake(0, 0, 320, 367)];
dispatch_queue_t downloadQueue = dispatch_queue_create("flickr downloader", NULL);
dispatch_async(downloadQueue, ^{
NSArray *images = [FlickrFetcher photosInPlace:self.place maxResults:MAX_PHOTOS];
dispatch_async(dispatch_get_main_queue(), ^{
self.view=tableView;
self.imagesData = images;
});
});
dispatch_release(downloadQueue);
}
and imageData setter calls reloadData on table view.

Create a UIViewController (not a UITableViewController - even if you are about to present a table)
Create your "Loading" view and add it as an outlet
Create your tableview and add it as an outlet too
When the controller is presented set its view property to point to your "Loading" view. When all tasks have completed, switch the view with the tableview
Just don't forget to add protocols and methods involved in order to control your tableview
I hope that this will help you

Another answer to show an activity indicator inside a UITableViewController, without changing it to a UIViewController.
In the storyboard, add an activity indicator inside the table view (but not above the TableViewCell !). It will be inside the table view header (not section header !)
Connect the outlet of the activity indicator to your class file
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
Use these functions to show or hide the activity indicator
// enable tableview and hide the activity indicator
func activateScreen() {
self.tableView.tableHeaderView?.frame = CGRect(x: 0, y: 0, width: tableView.frame.width, height: 0)
self.tableView.tableHeaderView = self.tableView.tableHeaderView // necessary to really set the frame
activityIndicator.stopAnimating()
}
// disable tableview animate the activity indicator
func disactivateScreen() {
self.tableView.tableHeaderView?.frame = tableView.frame
self.tableView.tableHeaderView = self.tableView.tableHeaderView // necessary to really set the frame
activityIndicator.startAnimating()
}
Enjoy !

Related

SearchBar in TableViewController

In my app I have a UITableViewController, and I want to add a searchbar to it. The tableview has 3 sections which expand when clicked on one of them. I added this code to viewDidLoad:
searchBar.showsScopeBar = true
searchBar.delegate = self
searchBar.frame = CGRectMake(0, 0, tableView.frame.size.width, 100)
self.view.addSubview(searchBar)
The problem is: the searchbar appears in the first section when you open it, and it should appear above the whole tableview, any suggestions?
Any help is appreciated!
You can add searchBar as a tableHeaderView of the TableView of your UITableViewController.
You can find pretty helpful example in Programming iOS 9 by Matt Neuburg, inside chapter II: Table Views and Controller Views.
let src = SearchResultsController(data: self.sectionData)
let searcher = UISearchController(searchResultsController: src)
self.searcher = searcher
searcher.searchResultsUpdater = src
let b = searcher.searchBar
b.sizeToFit() // crucial, trust me on this one
b.autocapitalizationType = .None
self.tableView.tableHeaderView = b
self.tableView.reloadData()
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition:.Top, animated:false)
He also warns, that adding search bar as the table view's header view may have an odd effect: it causes the table view's background color to be covered by an gray color, visible above the search bar when the user scrolls down. The official workaround is to assign the table view a backgroundView with the desired color.

How to SubView a ViewController into a SubView of another ViewController

I have two ViewController.
One ViewControllers contain a UITableView. And another contains a UIButton.
I have created a SubView Programmatically.Now i want to SubView the ViewController which contains the UITableView in other when i Press UIButton.
I searched all over the net but cannot find any stable solution.
Currently i am trying this:
bodyView =[[UIView alloc]initWithFrame:CGRectMake(0,120,containerView.frame.size.width,120)];
bodyView.backgroundColor = [UIColor redColor];
CustomTableVC *tableVC = [[CustomTableVC alloc]init];
[tableVC willMoveToParentViewController:self];
[bodyView addSubview:tableVC.view];
[self addChildViewController:tableVC];
[tableVC didMoveToParentViewController:self];
[containerView addSubview:bodyView];
You cannot.
You can only use the view property of your UIViewController to assign into UIView associated in your second UIViewController which is not recommended because UIViewController as per MVC pattern holds lot controller stuff which includes populating the view and resolving the inputs/touch, which is an overhead in your (using multiple of viewcontrollers without needed) case.
You need to use one UIViewController. Add UITableView only in it, and UIButton only in it. You only use one controller and multiple views.
The other approach, if you do not want to change your code, may also use ContainerView. But in that case you need to create separate ViewControllers for UIButton and UITableView. And if you want to fetch data inbetween the ViewControllers, that will be a huge pain and also a bad software design with so much coupling and less encapsulation.
i have tried this one and it's working for me.
#IBAction func moveToOther() {
var otherController = OtherViewController()
var bodyView = UIView(frame: CGRectMake(0,120, self.view.frame.size.width, 120))
bodyView.backgroundColor = UIColor.redColor();
bodyView.layer.borderWidth = 1.0
let tblCntrl = UITableViewController()
bodyView.addSubview(tblCntrl.tableView)
bodyView.clipsToBounds = true
otherController.addChildViewController(tblCntrl)
tblCntrl.didMoveToParentViewController(otherController)
otherController.view.addSubview(bodyView)
self.navigationController?.pushViewController(otherController, animated: true)
}
You should use only one view controller that contains both the table view & UIButton.
By default hide the table view.
Just hide the button and show the table view when the button is clicked.

UIActivityIndicatorView weird behavior when stopping and starting

I have a UIViewController and in its respective storyboard scene I have an activity indicator connect to the controller like this:
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
I want to hide this activity indicator when the view is first launched, then after the users presses a button, inside its #IBAction, I want to start animating the indicator. So I wrote this:
override func viewDidLoad() {
super.viewDidLoad()
activityIndicator.stopAnimating()
}
and in my #IBAction I have:
#IBAction func attemptLogin(sender: UIButton) {
// Start the activity indicator
activityIndicator.startAnimating()
}
But this code does not work. The screen is launched without showing the activity indicator but when the button is pressed, it does not start animating and it does not even appear.
I have tried setting the .hidden property of this object but didn't get anywhere. I do not really want to add this indicator programmatically. Is there a way to make it work?
Try out dynamically create UIActivityIndicatorView
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spinner.frame = CGRectMake(0, 0, 24, 24);
[spinner startAnimating];
[spinner release];
There's a couple of things you could consider here as the root of your problems:
The activity indicator is not attached to the IBOutlet
The button's target is not attached to the IBAction (put a breakpoint in your method to be sure)
The default state of an activity indicator is not animated, so your line: activityIndicator.stopAnimating() is redundant.
Depending on your view's layouts/constraints, the UIActivityIndicator might be outside of your screens frame. Try printing out the activity indicators frame with NSLog(#"%#", NSStringFromCGRect(activityIndicator.frame)) before you start the animation.
Hope this helps!

ios uitableview bounce went wrong

I implemented a horizontal table view and it looks like this
The category bar, Dining Shopping something, is a horizontal table view and here is the implementation code.
LogInfo(#"Show Category Table start");
// add categories to be subview controller
self.categoriesTable = [[UITableView alloc] init];
self.categoriesTable.transform = CGAffineTransformMakeRotation(-M_PI * 0.5);
[self.categoriesTable setFrame:CGRectMake(0, 64, SCREENWIDTH, 44)];
self.categoriesTable.showsVerticalScrollIndicator = NO;
self.categoriesTable.showsHorizontalScrollIndicator = NO;
self.categoriesTable.pagingEnabled = YES;
self.categoriesTable.delegate = self;
self.categoriesTable.dataSource = self;
self.categoriesTable.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.superView addSubview:self.categoriesTable];
LogInfo(#"Show Category Table Finished");
self.categoriesTable.backgroundColor= [UIColor redColor];
It works as expect but if I change view, for example, I click any button to go to other view and go back to this view. The tableview looks like this
This problem also happpens even if I disable the bounce effect of the table view. Does anyone know how to solve this problem? Thanks!
Rather than applying a transform to the table to make it horizontal, use a collectionView with a horizontal layout.
Edit: If you want to continue using your current setup, try disabling automaticallyAdjustsScrollViewInsets on your view controller.
self.automaticallyAdjustsScrollViewInsets = NO
Edit 2: If you're curious, since iOS 7 every view controller looks at its topmost/first scroll view and adjusts its contentInsets to compensate for the navigation bar transparency which is enabled by default. In this case of course, such behaviour isn't desired at all.

How do I hide a tableView searchBar

I want to have a searchBar in the tableView that is hidden by default, but if the user pulls down it appears.
So I have used the following code to implement this but when the view is first displayed the searchBar is momentarily visible and I would like avoid this brief flash. Adding self.tableView.contentOffset = CGPointMake(0, 0) to the viewDidLoad() method does not have any affect. Note that the tableView is behind a navigation controller which is why setting the Y offset to 0 effectively hides the searchBar behind the navigation bar. Any idea how to make sure the tableView's headerView is hidden when the view is first be displayed.
I have considered simply removing the headerView but then the user can't drag down to access it.
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
searchBar.frame = CGRectMake(0, 0, self.tableView.frame.size.width, 44)
if let tv = self.tableView {
if let headerView = tv.tableHeaderView {
headerView.addSubview(searchBar)
} else {
FLOG("No table header view is available so create one!")
let headerView = UIView()
let width: CGFloat = tv.frame.size.width
headerView.frame = CGRectMake(0, 0, width, 44);
tv.tableHeaderView = headerView
tv.tableHeaderView!.addSubview(searchBar)
}
}
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
dispatch_after(0, dispatch_get_main_queue()) {
self.tableView.contentOffset = CGPointMake(0, 0)
}
}
I think there's an ordering issue with viewWillAppear and viewDidLoad. As I understand, viewWillAppear gets called every time a view will be drawn and added to the view hierarchy, whereas viewDidLoad gets called when a VC loads its subview for the first time.
So, I suppose on your first run the view loads via the mainQueue with viewDidLoad which places your search bar at (0,44), then viewWillAppear gets called but actually causes a brief visual blip due to the call to move the table's offset on the mainQueue. But on subsequent calls, since viewDidLoad has already happened, only viewWillAppear gets called. And since the search bar doesn't first start at (0,44) and then needs to move to (0,0), there's no blip.
Rather than directly defining the frame for the search bar, try calling sizeToFit on it after adding it to the tableHeader. Secondly, I don't believe you have to instantiate the header view, just set it equal to the searchbar. Lastly, take the contentOffset call out of the dispatch_queue, it should be handled correctly by viewWillAppear as is.
I went deep into UISearchController for iOS8 recently, and documented the journey (in part at least). For the code I used, here's a link to the implementation file and the corresponding write-up I did. It's in ObjC, so you'll need to do a little translation. But it looks like:
self.searchController = [[UISearchController alloc] initWithSearchResultsController:self.filteredResultsTableViewController];
self.searchBar = self.searchController.searchBar;
// other code ...
[self.searchBar sizeToFit];
// other code ...
self.tableView.tableHeaderView = self.searchBar;

Resources