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];
Related
I have a controller named as "firstViewcontroller" where i have a UITableView named as "discoveredInstanceTableView". I want to load that UITableView in to another UIViewController named as "secondViewcontroller"
I have used the below code but it is not working, It says property "discoveredInstanceTableView" not found ...Anybody please help me:
In the firstViewcontroller:
IBOutlet UITableView *discoveredInstanceTableView;
In the Secondviewcontroller:
firstViewcontroller *vc1 = [[firstViewcontroller alloc]initWithNibName:#"firstViewcontroller" bundle:nil];
[self addChildViewController:vc1];
[self.myTableview addSubview:vc1.discoveredInstanceTableView];
What you asked is valid only if you curious to know why the above thing is not working answer would be
You are doing something that is not allowed, this can not be done as per the documentation.
However, If we forget about the right wrong approach, you probably adding a table view as a subview over a table view itself and I am sure you passing a table view to a table view which might not be allocated.
First think about the UITableView how it works? it simply a ScrollableView which display content over its cells.
Eventually would recommend you read about TableView
EDIT: From the Above Comments
IMPORTANT: You should not embed UIWebView or UITableView objects in
UIScrollView objects. If you do so, unexpected behavior can result
because touch events for the two objects can be mixed up and wrongly
handled.» As UITableView is a UIScrollView, this applies here as well.
Possible Alternatives of displaying TableView inside the SecondViewController
Use #Rajath Kornaya's Answer And In my opinion that is not right approach since whenever you required callback action like on cell tap, you want to display an alert(or something else), you can't get the delegate callback inside the SecondViewController
But there are so many other right approaches available, that you should follow up.
Create a TableView separately either programmatically or through the XIB/Storyboard
Add delegate and data source (methods which responds when something interesting happened e.g Cell going to populate called cellForRowAtIndexPath) to current SecondViewController
Define all required data source methods and write proper code.
If you required to do something on cell tap, add specific delegate method too.
But if you want to reuse the FirstViewController Class TableView simply create a CustomView and add TableView inside there and simply add that view to each view controller class.
I hope it may helps you!!!
declare in viewcontroller2
#property (nonatomic, strong) UITableView *table;
create table in viewcontroller1
tableView=[[UITableView alloc]initWithFrame:CGRectMake(10, 10, 250, 300) style:UITableViewStylePlain];
[self.view addSubview:tableView];
tableView.backgroundColor=[UIColor greenColor];
while calling viewcontroller2 pass table to viewcontroller2
ViewController2 *v2=[[ViewController2 alloc]init];
v2.table=tableView;
UINavigationController *navigation=[[UINavigationController alloc]initWithRootViewController:v2];
[self presentViewController:navigation animated:YES completion:nil];
in viewcontroller2 access the table using the global variable
[self.view addSubview:self.table];
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?
after some years I tried again to work with XCode to write some little apps for iOS.
My MainViewController contains these lines in viewdidload:
UIStoryboard* overviewStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
UIViewController *overviewController = [overviewStoryboard instantiateViewControllerWithIdentifier:#"Overview"];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:overviewController];
...
[self addChildViewController:nav];
[self.view addSubview:nav.view];
[nav didMoveToParentViewController:self];
the Controller behind the Overview contains the whole gesture recognition in view did load:
the property
#property (nonatomic, strong) UISwipeGestureRecognizer *swipeGestureUpDown;
viewdidload:
self.tableView.dataSource = self;
self.tableView.delegate = self;
// gesture recognizer top
self.swipeGestureUpDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedScreen)];
self.swipeGestureUpDown.numberOfTouchesRequired = 1;
self.swipeGestureUpDown.direction = (UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown);
[self.view addGestureRecognizer:self.swipeGestureUpDown];
and swipedScreen only an nslog:
- (void)swipedScreen:(UISwipeGestureRecognizer*)gesture
{
NSLog(#"somewhere");
}
THE overviewcontroller contains a tableView with custom cells.
The maincontroller passes this overviewcontroller as rootcontroller to a navigation, which should be slideUp if you swipeUp, and slideIn if you swipeDown. The maincontroller is calling the navigationcontroller with rootcontroller as you've seen above.
Nothing happens, no gesture is recognized, and in some tries it crashes with this message
unrecognized selector sent to instance
does somebody now what to do?
Answer to the question came up in the comments. Just consolidating it here.
There were a few issues.
First:
self.swipeGestureUpDown = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipedScreen)];
#selector(swipedScreen) is missing : at end of swipedScreen which makes it unrecognizable as the definition of the function is - (void)swipedScreen:(UISwipeGestureRecognizer*)gesture
Second:
self.swipeGestureUpDown.direction = (UISwipeGestureRecognizerDirectionUp | UISwipeGestureRecognizerDirectionDown);
Having single gesture recognizer for two directions of swipe does not work. For details see this. You will need to have a dedicated gesture recognizer for each direction.
Third:
Most important of all was trying to add Up and Down direction swipes on UITableView which won't work as long as scrolling is enabled in UITableView as it has its own default actions to handle these swipes which prevents it from being handled manually. But if you have very limited content in the table and don't need scrolling, you can set scrollEnabled to false which will make UITableView stop using the gestures and forward the gestures higher up the responder chain. Refer scrollEnabled description here. (UITableView inherits from UIScrollView.)
I have an UIViewController. Inside the view controller there is a custom UIView class object added as subview. Now, inside the custom UIView class, I have a search display controller. When I do this
SearchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:SearchBar contentsController:self]
it is giving warning as self is UIView object, not UIViewcontroller object, so search result is not showing.
I want to show search result from the UIView only.
How can I do that? Any kind of help is appreciated.
You need to first create a UIViewController with the custom view you need and then add this controller's view to your view. Something like:
[self.view addSubview:[[[UIViewController alloc] init] view]];
Remember that UISearchDisplayController have a UITableView and that needs a UIViewController as a delegate. Just a UIView will not do.
When working with views and view controllers in an iPhone app, can anyone explain the difference between loadView and viewDidLoad?
My personal context, is that I build all my views from code, I do not and will not use Interface Builder, should that make any difference.
I've found that often when I add init code to loadView, I end up with an infinite stack trace, so I typically do all my child-view building in viewDidLoad...but it's really unclear to me when each gets executed, and what is the more appropriate place to put init code. What would be perfect, is a simple diagram of the initialization calls.
Thanks!
I can guess what might be the problem here, because I've done it:
I've found that often when I add init code to loadView, I end up with an infinite stack trace
Don't read self.view in -loadView. Only set it, don't get it.
The self.view property accessor calls -loadView if the view isn't currently loaded. There's your infinite recursion.
The usual way to build the view programmatically in -loadView, as demonstrated in Apple's pre-Interface-Builder examples, is more like this:
UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];
And I don't blame you for not using IB. I've stuck with this method for all of Instapaper and find myself much more comfortable with it than dealing with IB's complexities, interface quirks, and unexpected behind-the-scenes behavior.
loadView is the method in UIViewController that will actually load up the view and assign it to the view property. This is also the location that a subclass of UIViewController would override if you wanted to programatically set up the view property.
viewDidLoad is the method that is called once the view has been loaded. This is called after loadView is called. It is a place where you can override and insert code that does further initial setup of the view once it has been loaded.
viewDidLoad()
is to be used when you load your view from a NIB and want to perform any customization after launch
LoadView()
is to be used when you want to create your view programmatically (without the use of Interface Builder)
Just adding some code examples to demonstrate what NilObject said:
- (void)loadView
{
// create and configure the table view
myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped];
myTableView.delegate = self;
myTableView.dataSource = self;
myTableView.scrollEnabled = NO;
self.view = myTableView;
self.view.autoresizesSubviews = YES;
}
- (void)viewDidLoad
{
self.title = #"Create group";
// Right menu bar button is to Save
UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStyleDone target:self action:#selector(save)];
self.navigationItem.rightBarButtonItem = saveButtonItem;
[saveButtonItem release];
}
To prevent an infinite loop from happening when you read self.view, call the class' super implementation when you load a view. The super implementation will allocate a new UIView for you.
- (void) loadView {
[super loadview];
// init code here...
[self.view addSubView:mySubview1]; //etc..
}
The easiest way to use loadView is to make some type of base view controller, like MyBaseViewController which is subclass of UIViewController. In it's loadView method create view in this way:
-(void) loadView {
if ([self viewFromNib]) {
self.view = [self viewFromNib];
} else {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
}
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.view.backgroundColor = [UIColor whiteColor];
}
And when you need to make some view controller you just use subclass of MyBaseViewController and in it's loadView controller you just call [super loadView] like this
//sucblass loadView
-(void) loadView {
[super loadView];
//rest of code like this..
UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
[self.view addSubview:myLabel];
[myLabel release];
}
loadView() is called when your controller is asked to create its self.view. You can do it by yourself like
self.view = [UIView alloc] init...];
Or your controller's parent UIController class has already a method name -loadView() which initializes your self.view into blank view. Then you can call
[super loadView];
I really recommend the second approach as it encourages the inheritance. Only if your view controller is not directly inherited from UIViewController.
The definition given by Apple on viewDidLoad mentioned that it is called after the controller’s view is loaded into memory. To put it in a simple term, it is the first method that will load.
You might be thinking under what condition will this method being fully utilized? The answer is, basically whatever you wanted the app to load first. For instance, you might want a different background color, instead of white, you could perhaps choose blue.