I have a class name AccountRecoveryViewController. Here in UITableView's "customcell" which is called AccountRecoveryExpandedCell in - (void)awakeFromNib method I simply add a customView called RIPasscodeView. And set all necessary parameter.
Like this:
- (void)awakeFromNib
{
RIPasscodeView *passcodeView = [[RIPasscodeView alloc] initWithFrame:CGRectMake (32, 144, 278, 35)];
passcodeView.innerSpaceValue = 20;
passcodeView.placeHolder = #"-";
passcodeView.borderColor = [UIColor colorWithRed:250.0f/255.0f green:110.0f/255.0f blue:40.0f/255.0f alpha:1.0f];
passcodeView.delegate = self;
[self addSubview:passcodeView];
}
In RIPasscodeView I set the UITextFields delegate methods (textFieldShouldReturn or shouldChangeCharactersInRange) and it's works fine.
But now I want to access those UITextFields delegate methods from my AccountRecoveryViewController. Because I have to change the position of my tableview (up a little bit) by tap gesture, but the UITextFields delegate methods which is not available in this AccountRecoveryViewController class.
How can I do that? If any one have any suggestion or query please knock me.
Thanks a lot in advance.
You have several options to do this.
Create your own Protocol, and handle on action from inside the RIPasscodeView to AccountRecoveryViewController.
Assign the UITextField delegate to your parent controller, by passing the parent controller reference.
Use KVO.
Use NSNotificationCentre to notify your parent controller.
Cheers.
Related
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.
I created a custom UITableViewCell (for this example, let's just say the subclass is MyViewCell) which has an Nib file associated to it MyViewCell.xib. The nib contains a UITableViewCell with one subview UIView (named cardContainer) that's simply a rectangle with a blue background. I want to add a drop shadow around the UIView, so I added set the layer properties in the -initWithCoder call:
#implementation MyViewCell
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
[self initView];
}
return self;
}
- (void) initView
{
UIBezier Path*shadowPath =[UIBezierPath bezierPathWithRect:view.bounds];
[self.cardContainer.layer setShadowColor: [UIColor blackColor].CGColor];
[self.cardContainer.layer setShadowOpacity: 0.8];
[self.cardContainer.layer setShadowRadius:3.0];
[self.cardContainer.layer setShadowOffset: CGSizeMake(2.0,2.0)];
view.layer.shadowPath = shadowPath.CGPath;
}
#end
The problem I'm having is that these layer properties aren't being drawn. If I call the -initView call within awakeFromNib or drawRect it's drawn as expected. My question: why doesn't my original code work? Where should I be calling initView? Is there some view lifecycle? I understand that the initWithCoder doesn't have the outlets connected, but it didn't crash at runtime.
I read through Apple documentation around Views and searched through the SO questions without finding an answer. I found this SO answer, but again doesn't explain.
Hey I found a better way to do this ,just add some runtime attributes for your subview cardContainer
like this
no more code in .m file anymore.
EDIT:
From:NSNibAwaking Protocol
Important: Because the order in which objects are instantiated from an archive is not guaranteed, your initialization methods should not send messages to other objects in the hierarchy. Messages to other objects can be sent safely from within awakeFromNib—by which time it’s assured that all the objects are unarchived and initialized (though not necessarily awakened, of course).
You need to add this,
self.cardContainer.layer.masksToBounds = NO;
I want to draw a chart in an UIView. The question is how do I get the data (Points) to the view. If I create a protocol and set the UIViewController as the delegate where in the UIView do I call the delegate methods (initWithFrame? might be to early, and the delegate might not be set, awakeFromNib? but the view is 100% created in code, it has no nib file) ..
initWithFrame? might be to early, and the delegate might not be set.
In fact, the delegate cannot be set by the time you're in initWithFrame:, since the first thing you do with an object after allocation is initialization, i. e. until the init method returns, you can't call (well, it's idiomatic not to do so, at least) any other methods.
What you have to do is have a loadData or reloadData method, that the delegate must call explicitly after having set itself as the delegate of your view. I. e., from the view controller, you can call it like this:
#implementation ChartViewController
- (id)init
{
if ((self = [super init])) {
chartView = [[ChartView alloc] initWithFrame:self.view.frame];
chartView.delegate = self;
[self.view addSubview:chartView];
[chartView reloadData];
}
return self;
}
Then, in your chart drawing view, implement - reloadData as follows:
- (void)reloadData
{
// Call the delegate here,
// then do the drawing
}
A better way is to use UIViewController instead of a UIView. Because your that view has to manage data. Managing data is a UIViewController's job.
make a protocol but dont call it delegate. call it dataSource :D
anyways, call it when you first need the data .... as late as possible.. NOT in init.. maybe in the setDataSource call.
or when you draw for the first time and see you have no data..
look at UITableView to see how he does it and imitate that
The prequel of this problem is here.
I am trying to create and set a custom delegate and datasource to my programmatically created UITableView. I've googled around, but couldn't find any clear solution for my problem.
Meanwhile, I've created a new class that conforms to UITableViewDelegate,UITableViewDataSource
protocols. In this class:
tableView numberOfRowsInSection: 20
tableView cellForRowAtIndexPath: cell.textLabel.text=#"Nominals";
Class that contains UIViews:
Method that creates UITableView:
-(IBAction)segmentValueChaged:(id)sender
{
if(self.segment.selectedSegmentIndex==0)
{
[self.coinageView removeFromSuperview];
[self.view addSubview:nominalsView];
[self populateNominals:self.subCountryID];
}
else
{
[self.nominalsView removeFromSuperview];
[self.view addSubview:coinageView];
[self populateCoinages:self.subCountryID];
}
}
-(void)populateNominals:(int)subCountryID
{
NominalsTableViewDelegate *del=[[NominalsTableViewDelegate alloc]init];
UITableView *nominalsTableView=[[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 372) style:UITableViewStylePlain];
[nominalsTableView setDelegate:del];
[nominalsTableView setDataSource:del];
[self.nominalsView addSubview:nominalsTableView];
[nominalsTableView reloadData];
}
Finally, I'm getting EXC_BAD_ACCESS.
The evil is in [nominalsTableView setDelegate:del]; [nominalsTableView setDataSource:del]; rows. What's wrong with them.
Help is much appreciated. Thanks in advance.
I am not sure if you are adding a new UITableView to your current view or not, but I will assume you are doing so.
If you have a class that conforms to the UITableViewDelegate. But when i needed to create a UITableView programmatically, I create a new UITableView class (with the .h and .m) then make a MutableArray and exposing it as a property to the parent view so it can set the data source.
From there, you can create an instance of the class along with the datasource (which you exposed from the child object). Finally, you then add the view onto your current view. This method you dont need to set the delegate because your child class conforms to the tableview delegate protocol.
If you are just modifying the data inside the current tableview then, you use a NSMutableArray and then change the data in it. After then do a
[self.tableView reloadData];
Hope this helps!
EDITED
I might have misunderstood your question, what you (most likely) need to do is create a property of the delegate class then create an instance of your delgate class and assign it to the property.
Then do a
[myTableView setDelegate:self.myProperty];
[myTableView setDatasource:self.myProperty];
This, I believe, would solve you bad access problem.
EDITED AGAIN
Create a property inside your .h of the tableview class as such:
#property (nonatomic, retain) NominalsTableViewDelegate *myDelegate;
From there then inside your .m file, you do something similar to this:
NominalsTableViewDelegate* delegateClass = [[NominalsTableViewDelegate alloc] init];
[self setMyDelegate:delegateClass];
[delegateClass release];
Then you can set your tableview datasource as such:
[myTableView setDelegate:self.myDelegate];
[myTableView setDatasource:self.myDelegate];
Note: I currently have no access to a machine to test this, but just something to point you towards.
I just witnessed a very strange issue where my view would ignore all of the delegate calls coming from a custom view because I called alloc/init on the item at the load. I'm curious as to why.
#synthesize customTextField;
-(void)viewDidLoad {
// by calling this alloc/init, none of the changes here actually update to the view
// everything is ignored from here on in.
// if I comment out the alloc/init line, everything works fine
self.customTextField = [[UITextField alloc] init];
self.customTextField.text = #"Some text";
// setting font and size as well
}
While I would still get calls to the text field delegate methods, none were linked to my specific text fields. I could not respond to just customTextField.
I do realize that calling alloc/init will give me a completely new instance of customTextField... but why wouldn't that new instance be linked to IB and my view?
Because IB linking != binding.
When you link a variable in IB, it's a simply sets the variable once on first load, that's it. It does no other special code to track any changes to it, for good reason.
For example:
You are designing a UITableViewCell, and if you have a cell that is selected, you must rearrange all of the content inside the cell. In this case, you determined it would be easier if you just recreated all of the subviews and re-added them into the view, so you do the following:
-(void) layoutSubviews {
if (cellIsSelected)
{
// custom button is an IBOutlet property, which is by default a subview of self
self.customButton = [UIButton buttonWithType:UIButtonTypeCustom];
[[self someSubView] addSubview:customButton];
}
else {
// where is customButton located now? is it a subview of self or `someSubView`?
self.customButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
// [self addSubview:customButton];
}
}
Thus, it is much easier for IB to say let's set this once, and let the programmer figure the rest out than for IB to try and track all changes made to an object and report them the to the UI.
viewDidLoad is called after your nib is loaded, and creating a new UITextField instance at this point will not be associated with your nib. If you're setting up new instances manually you also need to manually setup the delegates, and add them as subviews of your view.
The XIB file has no way of knowing that you are changing the reference. Consider the following piece of code
NSObject *myObjA = [[NSObject alloc]init]; //create object
NSObject *myObjB = myObjA; //assign reference <- this is the your case after xib load
myObjB = [[NSObject alloc]init]; //create object, myObjA still lives on.
It's basically the same that happens when you load your XIB file; You get the reference to the instantiated object (equals myObjB in above example). You can do with the reference what ever you please but you do not change the interface instance just by creating a new object.