Overriding a method that is not in super - ios

I've subclassed UITextField in order to show a tool bar with a button when the keyboard is shown.
UITextField SubClass:
- (void)methodName
{
//Do stuff
}
When the user presses the button and methodName is triggered inside the UITextField.
I want the same method to be triggered also inside the current ViewController for some more specific code additions relevant only to the current ViewController
I thought about getting a referral to the topviewcontroller and trigger my method from inside methodName in the UITextField subClass but it doesn't feel right.
What's the right way to do the above?

I would look into the delegation pattern. It may make sense to have your view controller be the delegate of your text field so it is informed when your text field is pressed.
Something like:
textField.delegate = viewController;
And have your view controller class implement the appropriate UITextFieldDelegate methods so that it is notified when editing begins/ends.

Related

Getting data out of custom UITableViewCell and back into UITableViewController

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

What is required of me to implement to be able to add new items to a tableViewController from another viewController?

I am trying to learn iOS development but have stalled a bit so I hope that there is some kind soul here who might be able to help me in the right direction.
Let's say I have a UITableViewController that displays a number of items, consisting of a title and subtitle ( Subtitle style of a Tableview Cell). Items.m/h only consist of two properties, title and subtitle and a init method to set the properties. In my app delegate i create some default items and pass them/set them to my tableViewController's property tvc.items, which is a NSMutableArray. What do I need to do / what components do I need, to be able to add more items and then display them in my tableViewController?
I started with the following:
Added a new view controller in the storyboard
Embeddade the viewController in a Navigation Controller
Added a Bar Button Item at my Table View Controller with an identifier of add
Ctrl + drag from BarButtonItem (add) to my new view controller selected modal segue
Created a new class AddNewItemViewController
Entered this as the class under the Identity Inspector for the new view controller
I then added two Bar Button Items, Cancel and Done (with cancel and done as identifiers) in the storyboard for the new View Controller
This was followed by me adding two UITextFields, one for the Title and one for the Subtitle
Ctrl + drag from these outlets into AddNewItemViewController.m, between #interface AddNewItemViewController () ... here ...#end (so they become Private? Should I drag it here or to AddNewItemViewController.h ?, What is the standard way for doing similar outlets?).
In AddNewItemViewController I added two properties, NSString's (nonatomic, copy) * title and *subtitle which I thought would keep the input data from an intended user.
So, after this I now want do two things, and it is here as it becomes difficult (for me at least):
Making so that by clicking on Cancel, one return to the Table View controller, ie a dismissed the modal .
Adding the data within the text fields to that NSMutableArray which is the datasource by clicking Done.
So what is required of me to do the last two steps?
Where should I ctrl + drag from the Cancel and Done (so there will be actions)? I guess they must be submitted to AddNewItemViewController.m, but what must be done to dismiss the modal (by clicking on the 'Cancel') and what should be called at or performed when clicking on 'Done'?
Which or what class (es) must know about the other class?
Last but not least, what should I send in the prepareForSegue call (which I guess I will need to have to use to send the input data back to the table view controller)?
Where to start and what methods should i learn about in order to achieve my mission?
Best Regards,
Rookie
much quesetions :)
I will beginn with the close action.
Have a look at the AppleDocumentation, dismissViewController with sender self (your AddViewController).
To store your data from AddViewController to your TableViewController, it's a better way to use delegation.
AddViewController.h
#protocol AddViewControllerDelegate;
#interface AddViewController : UIViewController
#property (nonatomic, weak) id<AddViewControllerDelegate>delegate;
#end
#protocol AddViewControllerDelegate <NSObject>
- (void) addViewControllerDidFinishTakingData:(AddViewController *)addViewController withTitle:(NSString *)title andSubtitle:(NSString *)subTitle;
#end
AddViewController.m
- (IBAction)done:(id)sender
{
NSString *title = ...;
NSString *subtitle = .. .;
[self.delegate addViewControllerDidFinishTakingData:self withTitle:title andSubtitle:subtitle];
}
TableViewController.m
#interface TableViewController ()<AddViewControllerDelegate>
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"yourIdentifier"])
{
AddViewController *addViewController = (AddViewController *)segue.destinationViewController;
addViewController.delegate = self;
}
}
Last but not least to implement your new delegate-method
- (void)addViewControllerDidFinishTakingData:(AddViewController *)addViewController withTitle:(NSString *)title andSubtitle:(NSString *)subTitle
{
// handle your data here (store to array)
// reload your table
}
Better way, to create a Class (Model) for every entry.
The simplest thing to do would be to assign tvc.items to the destinationViewController's property during prepareForSegue.
You are correct in thinking that the Cancel and Done buttons belong to the AddNewItemViewController.
In the action for Done, you could add the new item to the items array you passed in during prepareForSegue, then in the presenting view controller (the one you launched the modal from), during viewDidAppear just reload the table. It'll be called when the modal disappears.

UIButton target to be UIViewController not custom UIView

I have a UIViewController which creates a custom sub view. The sub view is a UIView object which has been subclassed a few times.
Within the subview class I create a custom init method:
-(id)init {
self = [super init];
if (self) {
// Init code
[self spm_correctGuessViewCustomInit];
}
return self;
}
And within this I create a button and a label. The question relates directly to the button and its target action.
What I would like is for the UIViewController to have the buttons action, not the subclasses UIView (which actually creates and holds the button).
[continueButton addTarget:self.superview action:#selector(correctGuessContinueButtonPressed) forControlEvents:UIControlEventTouchUpInside];
I pass in the target of self.superview, this appears to work correctly and the correct method is run. However, I am shown a warning in the subclass 'Undeclared selector 'correctGuessContinueButtonPressed''
So am I implementing this approach correctly? Please let me know if more information is required.
One solution would be to update your custom view's init method so it takes target and action parameters (much like the addTarget: method of the button). You could then pass these values to the button via the addTarget: call.
- (instancetype)initWithTarget:(id)target action:(SEL)action {
// your normal init code here
// use target and action to setup your button
}
Your view.superview approach will not bring you to the view controller, but to a view.
You can import the header of your implementing class to fix the warning, but I think your design should be improved. Views should work pretty much on their own and not depend on their superviews, or even worse the whole architecture of views and controllers.
I'd pass a delegate down the line that gets called when the user pressed the button, or set some blocks on the views that get called when buttons fire.
Avoid communication over several layers of abstraction.
Reference previous similar question: Calling a method in a UIViewController from a UIButton in a subview
I had to add an import to the View Controller that the method was on, within the custom UIView subclass.
With the controller property set I could set the button target as controller
Ensure that the method that was being called from the button was in the controller header file, so could be seen by the subview implementation file. Previously this was not so the subview was not to know this existed.

Custom UIView inside a UIViewController button click to refresh UIViewController [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have a custom UIView which has been loaded into a different UIViewController. I have a button in the custom UIView, which calls a method inside its own class. I want this method to refresh the UIViewController which the UIView is embeded in. Currently I have tried importing the UIViewController to the UIView initializing it and then calling a public method which i have put inside the UIViewController. Nothing that happens inside this method seems to affect it though, I have even tried changing the navigation bar title and that wont work. I know the method is getting called though as it comes up in my log.
Any help?
This is where a delegation pattern comes into action. If I have not misunderstood, you want to perform some action(refresh??) on a UIViewController based on some action in a UIView, which is in turn is a part of its own view hierarchy.
Lets say your custom view is wrapped in a class CustomView. It has a method named action that is invoked at some point. Moreover, lets assume that you have used CustomView instances to some view controllers, namely, MyViewController1, MyViewController2, etc, as a part of their view hierarchies. Now, you want to perform some action (refresh) in your VCS when action method is triggered from your CustomView instances. For this purpose, you need to declare a protocol in the header file of your CustomView and will have to register a handler of this protocol (commonly known as delegate) to an instance of the CustomView. The header file would look something like this:
//CustomView.h
//your include headers...
#class CustomView;
#protocol CustomViewDelegate <NSObject>
-(void)customViewDidPerformAction:(CustomView*)customView;
#end
#interface CustomView : UIView {
//......... your ivars
id<CustomViewDelegate> _delegate;
}
//.......your properties
#property(nonatomic,weak) id<CustomViewDelegate> delegate;
-(void)action;
//....other declarations
#end
Now, you would like customViewDidPerformAction method to be called from any class (like a view controller for "refresh" purpose) whenever action method is triggered. For that purpose, in your implementation file (CustomView.m), you need to invoke the customViewDidPerformAction method stub, if available, from inside your action method:
//CustomView.m
//.......initializer and other codes
-(void)action {
//other codes
//raise a callback notifying action has been performed
if (self.delegate && [self.delegate respondsToSelector:#selector(customViewDidPerformAction:)]) {
[self.delegate customViewDidPerformAction:self];
}
}
Now, any class that conforms to CustomViewDelegate protocol can register itself as a receiver of the callback customViewDidPerformAction:. For example, lets say, our MyViewController1 view controller class conforms to the protocol. So the header to the class would look something like this:
//MyViewController1.h
//include headers
#interface MyViewController1 : UIViewController<CustomViewDelegate>
//........your properties, methods, etc
#property(nonatomic,strong) CustomView* myCustomView;
//......
#end
Then you need to register this class as a delegate of myCustomView, after instantiating myCustomView. Say you have instantiated myCustomView in the viewDidLoad method of the VC. So the method body would be something similar to:
-(void)viewDidLoad {
////...........other codes
CustomView* cv = [[CustomView alloc] initWithFrame:<# your frame size #>];
cv.delegate = self;
self.myCustomView = cv;
[cv release];
////......other codes
}
Then you also need to create a method with the same method signature as the protocol declares, inside the implementation(.m) file of the same VC and right your "refresh" code there:
-(void)customViewDidPerformAction:(CustomView*)customView {
///write your refresh code here
}
And you are all set. MyViewController1 would execute your refresh code whenever action method is performed by CustomView.
You can follow the same mechanism (conform to CustomViewDelegate protocol and implement customViewDidPerformAction: method) from within any VC, containing a CustomView in its view hierarchy, to refresh itself whenever the action is triggered.
Hope it helps.

Delay in UITextFieldDelegate event

This is a strange problem but I am perplexed on how to solve this - I have a UITableView that has custom UITableViewCells. Each UITableViewCell has two UITextFields and each UITextField is linked to a delegate that processes the textFieldDidEndEditing event. This works perfectly except in one instance.
Problem
The screen also has a 'Save' button and the problem arises when the user edits a UITextField and directly clicks the 'Save' button without clicking elsewhere in the screen. In such an event, the saveAction method is invoked before the textFieldDidEndEditing event and as a result the last edit of the user is lost.
I tried to debug using NSLog statements and found that while the textFieldDidEndEditing is indeed getting called, it is called after the saveAction event.
I thought about calling the textFieldDidEndEditing event from saveAction but that didnt make sense as I would have no idea about which UITextField is being edited.
Any suggestions are very much appreciated.
you could make a note of the text field that is active when the –textFieldDidBeginEditing: delegate method is called in your view controller
have an assigned property that points to the active text field and then in -saveAction send it -resignFirstResponder.
header:
#property (nonatomic, assign) UITextField * editingTextField;
m file:
-(void)textFieldDidBeginEditing:(UITextField *)textField{
self.editingTextField = textField;
}
-saveAction{
if(self.editingTextField)
[self.editingTextField resignFirstResponder];
//continue implementation
}

Resources