I have a UIViewController(A) that uses an NSObject with delegate methods to display a UITableView programmatically. This works great.
But then when I select a row I want to load a different UIViewController(B). I have tried to use a NSNotification on UIViewController(A) that is called in the didSelectRowAtIndexPath. However this takes about 20 seconds to load the UIViewController(B).
NOTE: I know there is nothing wrong with UIViewController(B) in terms of loading.
I have checked that the displaying of UIViewController(B) is on the main thread. Which it is.
I am therefore thinking that there may be an alternative method to display UIViewController(B). The alternative method I am thinking is to write a method in UIViewController(A) to present UIViewController(B). But how can I call this from the NSObject didSelectRowAtIndexPath method.
Managed to get it to work. So for anyone who is having the same troubles. My answer came from https://github.com/burczyk/KBContactsSelection
Related
I'm trying to learn how delegates work and wrap my head around the concept. I'm finding I get some of the ideas. I understand you use it to pass data from one view controller to another, however wouldn't it work the same if I just sent data from a segue and every time the 1st view controller would appear, it would use that data?
So for example I have 2 view controllers.
1 is homeViewController and
2 is editViewController.
I have a variable titled "addressOfHome" which is a string in homeViewController(1).
In homeViewController under the method "viewDidAppear"
I also set the addressLabel = addressOfHome.
Then I just pass the data from editViewController(2) to homeViewController(1)
when the segue's destination vc is homeViewController ?
I'm terrible at explaining things so I apologize for that, but I gave it my best shot. Thanks for your time!
Delegates are mainly used to "trigger action" on an object from another one.
An object delegates a way to handle something to someone else, for example when you click on an UIAlertView button, if its delegate is set on a viewController, alertView:clickedButtonAtIndex will be executed on the VC, which can so react as it want
I'm terrible at explaining things
Haha, yes, you are !
A delegate isn't for that - a delegate is a way to over-ride the default behaviour of some feature(s) of a class, without creating your own sub-class. See this post : How does a delegate work in objective-C?
Are you trying to understand how delegates work (in which case, I don't think your example is one that requires a delegate) or are you trying to implement the functionality you describe, and think that a delegate is the way to do it? (I think you actually want a data source).
I am working in swift. I have a protocol defined that has a function called reload. I have a home class which is a tableviewcontroller where I extend the protocol and implement the reload function. I have another class where there is a button. On this click of the button I set the delegate and the reload function is called. Until this step it works fine. Now in the reload function i want to refresh the home page, so i tried
tableview.reload()
this is not working and the app is crashing, then i tried calling
viewDidLoad()
this is also not refreshing the page. I dont want to use NSNotification
what am i doing wrong?
Can anybody please help
Thanks in advance
Are you sure your UITableView data source delegate methods are working properly? You should look up viewDidLoad() and viewWillLoad(), etc... in the UIViewController reference page (Apple doc). If you're serious about app development I recommend at the very least making the time to spend 15 - 30 minutes each looking at the UIViewController and UIView pages to familiarize yourself with them, since you'll be using them all the time. You should know basically how they work. Also check out CALayers in UIViews. Then you'll be better established to write really nice apps.
you are setting a delegate on button touchup inside event, please check whether you are setting a delegate to your Home class .
I assume your mean tableview.reloadData() which is the correct method to call for reloading your tableview.
You shall not call viewDidLoad by yourself.
Right now I have a view controller that handles a lot of network requests. They are each a subclass of a NetworkRequest class and this view controller is the delegate of all of them. It implements one callback function, networkRequestDidFinish.
The problem is that all these network requests are separate objects, and they will all call that same function. What is the proper way to design this? Right now I go through a bunch of if statements in networkRequestDidFinish to see what kind of network request returned. It feels wrong though, but I am not sure what is conventional to do in this case.
Thanks.
One useful pattern here is to be sure that the delegate methods pass self to the view controller. It sounds like you might already be doing this - if you're using a series of if statements, you probably have a pointer to the relevant NetworkRequest. If you aren't, or are not sure, read on.
You see this pattern pretty much wherever delegation is used. As an arbitrary example, take the UITableViewDelegate protocol. The first argument of each of the delegate methods is a UITableView. For example:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
When a UITableView instance calls this delegate method, it passes self as that first argument. It does something like:
[self.delegate tableView:self heightForRowAtIndexPath:0];
Then, the delegate knows which UITableView it's dealing with, because it has a pointer dropped in its lap, as the argument tableView.
In your case, I would start by adding a parameter to the delegate method networkRequestDidFinish, changing its signature to:
- (void)networkRequestDidFinish:(NetworkRequest *)networkRequest
That way you can tell which instance of NetworkRequest has called the delegate method.
Already had that, or that's not good enough? Well, the next thing I'd say would be to consider whether you really need to perform different actions based on the actual class of the NetworkRequest instance that's calling the delegate method. If you're just passing along the data, the answer is probably no. For example:
- (void)networkRequestDidFinish:(NetworkRequest *)networkRequest {
[self processData:networkRequest.data];
}
That method doesn't care what class networkRequest really is. But you seem to care, since you're doing "a bunch of if statements." Then I would say that it might be a mistake to have them all hitting one delegate method. Instead, you might want to get rid of a delegate on NetworkRequest, and instead add a protocol to each of the subclasses of that class, specific to the subclass.
What?
Let's look at an example.
Imagine that one of the subclasses of NetworkRequest is FooNetworkRequest which, of course, requests foos. Its header might look like this:
// stuff...
#protocol FooNetworkRequestDelegate
- (void)fooNetworkRequestDidFinish:(FooNetworkRequest *)fooNetworkRequest;
#end
#interface FooNetworkRequest : NetworkRequest
#property (weak, nonatomic) id<FooNetworkRequestDelegate> delegate;
// stuff...
#end
You apply a similar treatment to all the other subclasses of NetworkRequest. Then, your view controller would adopt each of these protocols, and have a separate method for each subclass of NetworkRequest.
That still seems kind of dirty, right? It does to me. Maybe this is a hint that your view controller is trying to handle too many things at once. You should consider trying to spread out the responsibility for all these NetworkRequest subclasses to multiple view controller or model classes.
If that's not an option, you can at least make your view controller's source a little easier to read by using one or more categories. Put your view controller's main behavior in its .m file, as usual, and then create a category on that view controller that adopts the proper protocol(s) and handles the requests.
There are generally 2 nice procedures.
You can use block instead of the delegate. That means you can send a block to your request class either when instancing it or when you make the request.
Use a target/selector pair system to make it look kind of like adding a target to an UIButton. NSInvocation should do the trick.
I has many UITableViewController subclasses in my app.
Now i just needed to modify them all to add +1 row in all cases, and one simple equal row in all.
I do not want to modify all of them by hand, better way seem's to replace UITableViewDataSource method to modify values in way like:
+(void)load {
[[self class] jr_swizzleMethod:#selector(tableView:numberOfRowsInSection:) withMethod:#selector(swizzledTableView:numberOfRowsInSection:) error:nil];
}
- (NSInteger)swizzledTableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self swizzledTableView:tableView numberOfRowsInSection:section] + 1;
}
But it replaces superclass function, that does not called in subclasses, so this is not working. Is there method to do that what i want, without modifying all subclasses?
You'd need to swizzle every subclass specifically. You can find them by introspecting the class hierarchy at runtime with objc_getClassList, but I can't begin to describe how dangerous and fragile this approach is. You're trying to apply this to every tableview in the system, which you hope is just the tableviews you mean it to be (i.e. your tableviews). But what about tableviews that might be used by the system or from third-party libraries? You're modifying them, too. And when you try to understand the crash this causes, the stack trace will be unintelligible because of the swizzle.
In order for this to work, tableView:cellForRowAtIndexPath: also needs to correctly handle this extra row, so it's hard to see how every table view controller in the system is going to be implemented correctly without knowing about this +1.
Either subclass your table view controller (and have them call super), or use a separate object that all of them call to add the extra row if it's needed. This other object (or superclass) is also where you should handle the cell for this extra row.
I have little experience in swizzling. But I have two possible solutions to your problem.
First:
Create a subclass: YouBaseTableView: UITableView, and add a row in YouBaseTableView. And inherit all your table view classes from YouBaseTableView.
Second:
Create an extension for UITableView, and write your row in this extension.
I'm probably late for the train...
But for future reference, a solution for the problem would be to swizzle setDataSource of UITableView and replace it with an NSProxy instance.
Usually nobody overrides the setDelegate / setDataSource methods, and that would allow you to swizzle those and intercept all calls to these delegates and exchange the implementation.
Check this out for more info: https://developer.apple.com/documentation/foundation/nsproxy
You are going into two different areas that need full understanding to be used correctly and are highly dangerous: Performing code in the +load method, and using method swizzling. I would never dare doing anything in +load. +initialize is ok if you know what you are doing, but +load is something you mustn't even think of touching if you ask questions here.
Now ask yourself first: What is "self" in a class method, and what is "[self class]"? Do you think this has even a chance of working?
I'd also recommend that you google for "swizzle" and pick up some other code for method swizzling. It looks quite dubious to me. And writing it as a category instead of a plain C function feels just horrible.
I have a method in AppDelegate which get some data from server, this method get called every time when application become active. I want to reload some table in another view when server data received successfully. How can i do this without using NSNotification?. I know passing notification can do this job. I want to know is there any other way to perform this?
well comments by users already explained what to do.
But i had another approach.I don't prefer, but it will work.
Always set some constant tag value to your table view (say 1001).And make sure than you never use the same tag on others.
Then in that method of appDelegate, you can do->
UITableView *tableView =(UITableView*)[self.window viewWithTag:1001];
[tableView reloadData];
I am reminding you again, don't use this. use NSNotification class.
Although it creates an unneeded dependency between the view and the app delegate, I'd implement this way:
create a ViewReloader protocol with a method - (void) reloadTable;
implement that protocol in the view
add a property of type id<ViewReloader> to the app delegate
when the view is instantiated, assign it to the property defined above
when you need to reload, call the reloadTable method of the id<ViewReloader> property from the app delegate (but always check for property != nil)
if the view is destroyed/deallocated, remember to reset the app delegate property
Well samething you can do with using custom delegates as well. If you want to pass message from one object to another. You can use notification also, but use only when you want to broadcast the message.