I currently develop an iOS library which tracks user's interaction such as button tap, text editing and so on.
For tracking a table view's row selection, I use method swizzling to wrap tableView:didSelectRowAtIndexPath: delegate method.
When tableView:didSelectRowAtIndexPath: delegate method is defined in an application that use the library, it works well.
However, when tableView:didSelectRowAtIndexPath: delegate method is not defined, I add it dynamically to the delegate instance but tableView:didSelectRowAtIndexPath: delegate method is not called by a table view.
// Add an original tableView:didSelectRowAtIndexPath: if not exists.
if ( ! [delegate respondsToSelector:#selector(tableView:didSelectRowAtIndexPath:)] )
{
BOOL result = class_addMethod( [delegate class], #selector(tableView:didSelectRowAtIndexPath:), (IMP)tableViewDidSelectRowAtIndexPathIMP, types );
if ( ! result )
return;
}
When I call the added method manually, it responds.
Any ideas?
Related
I have a UITableView comprised of custom UITableViewCells. In each cell, there is a UILabel and a UISlider. Does anyone know how to, upon a change in value of one of the sliders, send the new value of the slider from the custom UITableViewCell (in a separate file) to the UITableViewController, so that I can then update the array from which the table was populated?
The closest I've got so far is a failed hack: firing a setSelected event when a slider value is changed. Whilst this highlights the changed custom cell, the event is not picked up by didSelectRowAtIndexPath in the UITableViewController.
Whilst code is always appreciated, a conceptual/method solution is what I am looking for.
Thank you in advance,
Jamie
What you need is called Delegate Pattern.
Quoting from there to explain what does it mean:
Delegation is a simple and powerful pattern in which one object in a
program acts on behalf of, or in coordination with, another object.
The delegating object keeps a reference to the other object—the
delegate—and at the appropriate time sends a message to it. The
message informs the delegate of an event that the delegating object is
about to handle or has just handled. The delegate may respond to the
message by updating the appearance or state of itself or other objects
in the application, and in some cases it can return a value that
affects how an impending event is handled. The main value of
delegation is that it allows you to easily customize the behavior of
several objects in one central object.
These diagrams will help you understand what goes on:
Architecture:
Operation:
Now as to how to implement it, this is what you have to do.
For Objective-C:
First of all, create delegate methods of your UITableViewCell. Lets name it ContactTableViewCell.
In your ContactTableViewCell.h file, do this:
#protocol ContactCellDelegate <NSObject>
#required
-(void) didMoveSliderWithValue:(float) value;
#end
#interface ContactTableViewCell : UITableViewCell
#property (weak, nonatomic) id<ContactCellDelegate> delegate;
Now conform your TableViewController to this delegate. Let's name your VC MyTableViewController.
In MyTableViewController.h, Do this:
#interface MyTableViewController : UIViewController <ContactCellDelegate> //Use UITableViewController if you are using that instead of UIViewController.
In your cellForRowAtIndexPath, before returning cell, add this line:
cell.delegate = self;
Add implementation for the delegate method INSIDE your MyTableViewController.m.
-(void) didMoveSliderWithValue: (float) value
{
NSLog(#"Value is : %f",value);
//Do whatever you need to do with the value after receiving it in your VC
}
Now let's get back to your ContactTableViewCell.m. In that file you must have added some IBAction to capture the value change event in slider. Let's say it is the following:
- (IBAction)sliderValueChanged:(UISlider *)sender {
self.myTextLabel.text = [#((int)sender.value) stringValue]; //Do whatever you need to do in cell.
//Now call delegate method which will send value to your view controller:
[delegate didMoveSliderWithValue:sender.value];
}
When you call delegate method, it will run the implementation that we wrote earlier in the MyTableViewController. Do whatever you need in that method.
What happens here is that your Cell sends the message to your desired VC (Which is delegate of the Cell), that "Hey, Call the delegate method that we wrote earlier in your body. I am sending you parameters right away". Your VC takes the parameters and does whatever you wanted it to do with that info and at that time.
For Swift:
First of all, your TableViewCell.swift file, create a protocol like this:
#class_protocol protocol ContactCellDelegate {
func didMoveSliderWithValue(value: Float)
}
Now in your Cell class, create a delegate property like:
var cellDelegate: ContactCellDelegate?
In your Slider IBAction, call the delegate method like this:
self.cellDelegate?.didMoveSliderWithValue(slider.value)
In your VC do these changes:
Make it conform to the delegate:
class MyTableViewController: UIViewController, ContactCellDelegate
Add this line before returning cell in cellForRowAtIndexPath
cell.cellDelegate = self //Dont forget to make it conform to the delegate method
Add the implementation of required delegate method:
func didMoveSliderWithValue(value:float) {
//do what you want
}
I have kept the Swift part precise and summarized because It should be very easy to change the detailed Obj-C explanation to Swift implementation. However If you are confused about any of the pointers above, leave a comment.
Also see: StackOverflow answer on using Delegate pattern to pass data back
I am stuck in a UITtextfield text issue. It's a split view app and the detail view is the table view having custom cell. In Custom cells I have a textfield and when I tap on the textfield it will navigate to another table having list of values, from which I need to select a value.
My issue is when I tap on a row in master view I need to check whether any textfield's text changed. If yes I am showing an alert view to user saying "some change is there do you want to save it?".
User UITextFieldDelegate, or
Try this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(methodNothing)
name:UITextFieldTextDidChangeNotification object:myTextField];
And you can use:UITextFieldTextDidBeginEditingNotification or UITextFieldTextDidEndEditingNotification
You can set a delegate for your NSTextField instance and have the delegate implement the following method:
- (void)controlTextDidChange:(NSNotification *)notification
{
// there was a text change in some control
}
Your delegate object can be the application delegate, a window controller, a view controller, or some other object in your application. The delegate can be programatically set via
[myTextField setDelegate:delegateObject];
or, in Interface Builder, via the delegate outlet available in the NSTextField control.
Note that if there are multiple controls hooked to the same delegate then -controlTextDidChange: will be sent for each control, i.e., the same method is called for different controls. If you want different behaviour according to the control where the text has changed, you can use -[NSNotification object] to identify the control that has sent the notification.
For instance, if you have two text fields with corresponding outlets nameField and addressField, and you’ve set the same delegate for both fields, then:
- (void)controlTextDidChange:(NSNotification *)notification {
// there was a text change in some control
// [notification object] points to the control that has sent
// the notification
if ([notification object] == nameField) {
// nameField has changed
}
else if ([notification object] == addressField) {
// addressField has changed
}
}
Alternatively, you could have one delegate for each text field. In this case, there’d be no need to test [notification object].
Does UIPopOverController have a tag property?
I have multiple UIPopOverControllers, how do I distinguish between them from the delegate methods?
Thanks.
No, tag properties are only on views and bar button items, not view controllers.
However all UIPopoverViewControllerDelegate methods get passed a popoverViewController variable. You can tell which one is calling the delegate method by comparing that variable to your popover controller objects.
- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
if (popoverController == myFirstPopoverController) {
// do something
}
}
Note that this is true for all delegate methods in iOS, UITableViewDelegate methods all receive a tableView variable and so on.
According to the apple documentation there is no tag property. The tag property comes from being a subclass of UIView which UIPopoverController is not. In fact, UIPopoverController inherits directly from NSObject.
When your delegate callbacks run they will pass in exact instance of whichever UIPopoverController invoked the callback.
You can observe this on the UIPopoverControllerDelegate documentation with the following two protocol methods:
– popoverControllerShouldDismissPopover:
– popoverControllerDidDismissPopover:
I got a problem.
That when I initial a Controller I used initwithnib:bundle: method and then I think this controller has been loaded to the memory. So I call its method selectUnreadMessage to change its header. But I found that viewdidload was called after selectUnreadMessage.
Some one can tell me why ? Thanks.
- (void)selectUnreadSegmentedHeaderButton {
if ([YDNetworkingManager sharedNetworkingManager].badgeNumbers.count) {
for (NSNumber *unreadMessagesCount in [YDNetworkingManager sharedNetworkingManager].badgeNumbers) {
if (unreadMessagesCount.intValue > 0) {
NSInteger index = [[YDNetworkingManager sharedNetworkingManager].badgeNumbers indexOfObject:unreadMessagesCount];
self.segmentedHeader.selectedIndex = index;
[self loadTableViewAtIndex:index];
break;
}
}
}
}
The code of my private method is above. This controller has the property SegmentedHeader to switch between different kinds of messages.
This method is called After the Controller which used this method as addSubView;
viewDidLoad method is called when view of Controller is preparing to be addSubView into another view. And when view of Controller was added, viewWillAppear and viewDidAppear will be called. If view of Controller is removed from superview, viewWillDisAppear and viewDidDisAppear will be called.
And view of Controller is added again, viewDidLoad will NOT be called. Other methods will call the same as I've said.
It means when you initial a Controller, viewDidLoad was not called immediately. This method will be called later. It is lazy-loading
Hope it useful!
ViewController initialization is not creating view - viewcontroller's view is loaded lazily - when it's view is actually added to view hierarchy. So, -viewDidLoad method gets called when you access viewcontroller's .view property - thus, you may consider initializing your data structures in the init method (fetch data, allocate containers, setup default values, etc.) and later, when -viewDidLoad is called - apply you data to your views / UI.
I am designing a one-to-one chatting interface using table view. This table view is modified to show bubbles as some new message arrives. The new message arrives through a push-notification in this case. I call following code in my function which receives message through the push notification:
-(void)messageReceived: (NSString *)message{
_message=[message retain];
[tableView reloadData];
}
However, it seems this does not reload my table view.
If I place the call for reloadData in the viewDidAppear function, it reloads fine. It also reloads fine, if I place the reloadData call in a function whose return type is IBAction (ex: a function binding to button click)
What could be the reason for reloadData to not get triggered through custom declared functions ?
reloaddata method is called but the trick here that you didn't add the incoming message to the datasource that the tableview load from !
may be you have not Connect the Tableview with table view Delegates and Datasource
Objective-C
#interface YourClass : UIViewController <UITextFieldDelegate, UITextViewDelegate>
yourtableview.delegate = self;
yourtableview.dataSource = self;
[tableView reloadData];
Swift 3
class YourClass: UIViewController , UITableViewDelegate, UITableViewDataSource
yourtableview.delegate = self
yourtableview.dataSource = self
yourtableview.reloadData()
the other way is! for Swift and Objective-C both.
Right Click on the Table view and drag and drop the delegates.