Custom UIControls with Storyboard - xcode

I'm having trouble figuring this one out. I have a custom UIControl class set up to hold a UIImage and UILabel, and my UITableViewCell class holds two of these UIControls (leftProduct, & rightProduct)
#interface FeaturedProductControl : UIControl
#property (strong, nonatomic) IBOutlet UIImageView *featuredProductPhoto;
#property (strong, nonatomic) IBOutlet UILabel *featuredProductDescription;
#property (strong, nonatomic) Product *featuredProduct;
- (id)initWithProduct:(Product *)product;
#end
#interface FeaturedTableCell : UITableViewCell
#property (strong, nonatomic) IBOutlet FeaturedProductControl *leftProduct;
#property (strong, nonatomic) IBOutlet FeaturedProductControl *rightProduct;
#end
The images and labels are being filled in using the init method during cellForRowAtIndexPath, and they're coming through just fine. I have a target action associated with the UIControls in the Storyboard, but the productClicked: method doesn't seem to be called. I've tried changing it out to add the target action programmatically, no luck.
However, if I add an alloc/init to the code, the productClicked: method triggers properly, but unfortunately the UILabel and UIPhoto now come up empty onscreen. Since the UIControls are designed in Storyboard, I think I'm not supposed to do the alloc calls myself, but the TableViewController doesn't seem to like that it's not being called. I've tried calling alloc within [[FeaturedTableCell alloc] init], but it had no effect.
Contents of cellForRowAtIndexPath:
cellIdentifier = #"Featured Row";
FeaturedTableCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[FeaturedTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
FeaturedRow *featuredRowData = [self.productIndexModel objectAtIndexPath:indexPath]; // This contains the necessary products to fill the Row
Product *leftProduct = [featuredRowData.skuList objectAtIndex:0];
cell.leftProduct = [[FeaturedProductControl alloc] initWithProduct:leftProduct]; // Actions trigger, but no data
// cell.leftProduct = [cell.leftProduct initWithProduct:leftProduct]; // Data is filled in, but no action
// [cell.leftProduct addTarget:self action:#selector(productClicked:) forControlEvents:UIControlEventTouchUpInside]; // the storyboard mapping works without this line of code
if (featuredRowData.skuList.count > 1)
{
Product *rightProduct = [featuredRowData.skuList objectAtIndex:1];
cell.rightProduct = [cell.rightProduct initWithProduct:rightProduct];
// cell.rightProduct = [[FeaturedProductControl alloc] initWithProduct:rightProduct]; // Yes, these two are reversed from the left side code above for testing
[cell.rightProduct addTarget:self action:#selector(productClicked:) forControlEvents:UIControlEventTouchUpInside];
cell.rightProduct.hidden = NO; // right side column is hidden in case the number of products is odd
}
else
{
cell.rightProduct.hidden = YES;
}
[cell setNeedsLayout];
return cell;
Any idea what I'm doing wrong? I'm trying to keep as much of the initialization and setup as possible inside the storyboard, so I'd prefer not to go back to writing the whole UIControl programmatically.
Thanks everyone!

Figured it out. In my initWithProduct method, I included
self = [super init];
I commented out this line and the Touch Up Inside events started firing normally.

Related

IOS 7 - Update an UILabel with value computed in background using delegation

I am having a problem trying to update UILabel.text using a value calculate in background using delegation (which seems to be slow).
#interface BTAViewController : UIViewController <ExchangeFetcherManagerDelegate>
#property (weak, nonatomic) IBOutlet UILabel *bidPriceLabel;
#property (strong, nonatomic) FetcherManager *fetcherManager;
#end
my BTAViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
self.fetcherManager = [[FetcherManager alloc]init];
double calculateValue = [self.fetcherManager fetchPricesFor];
self.bidPriceLabel.contentMode = UIViewContentModeRedraw;
[self.bidPriceLabel setNeedsDisplay];
self.bidPriceLabel.text = [[NSString alloc] initWithFormat:#"%f", calculateValue];
}];
The label has been correctly initialised in a method not shown here and it shows the correct default value at startup. Once I execute this code, the label will get the value 0.00000000, because my calculateValue has not been calculate yet.
How can I wait for this value to be calculated and then display it has text of the label ?
I can't post images because I don't have enough reputation...
Thanks
Are you sure the value calculated is not purely 0 or null?
What I would do is to add a delegate callback function in BTAViewController.m from FetchManager
Something like this:
BTAViewController.h
//add delegate callback function under #interface
-(void)valueReturned:(float)someValue;
BTAViewController.m
-(void)valueReturned:(float)someValue{
//updates value when the delegate returns the value
self.bidPriceLabel.text = [[NSString alloc] initWithFormat:#"%f", calculateValue];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.fetcherManager = [[FetcherManager alloc]init];
self.fetchManager.delegate = (id)self;
[self.fetchManager getPricesFor];
// double calculateValue = [self.fetcherManager fetchPricesFor];
self.bidPriceLabel.contentMode = UIViewContentModeRedraw;
[self.bidPriceLabel setNeedsDisplay];
[self.view addSubview:self.bidPriceLabel];
}];
In FetchManager.h and .m
FetcherManager.h
#property (retain,nonatomic)id<ExchangeFetcherManagerDeelgate>delegate;
-(void)getPricesFor;
FetcherManager.m
#synthesize delegate;
-(void)getPricesFor{
float price;
//calculate price
[self.delegate valueReturned:price];
}
Add the label to the view, set it to be hidden (bidPriceLabel.hidden = YES). Then, create and add a method for the delegate (BTAViewController) which when fired, checks the hidden property of the UILabel, and if it is TRUE (is hidden), set bidPriceLabel.hidden = NO which will then display the UILabel as you intended to.
Call this method in FetchManager once you are done retrieving the information.

iOS - Accessing the name of IBOutlet

I am having several IBOutlets of UIView; several subviews within my parent view.
I am trying to loop over all the subviews which are UIView.
I want to loop over all subviews that have the name of their IBOutlet starting with "view_"
Can somebody help me with this?
Question is I really want to access the name of the IBOutlet.
Here's my code for clarification:
#property (weak, nonatomic) IBOutlet UIView *view_player1;
#property (weak, nonatomic) IBOutlet UIView *view_player2;
#property (weak, nonatomic) IBOutlet UIView *view_referee1;
- (void)listSubviewsOfView:(UIView *)view {
// Get the subviews of the view
NSArray *subviews = [view subviews];
// Return if there are no subviews
if ([subviews count] == 0) return;
for (UIView *subview in subviews) {
NSLog(#"my subview name is: %#", subview.class); //?? is only giving my the type eg UIView, i want it to return view_player2
// List the subviews of subview
[self listSubviewsOfView:subview];
}
}
How can I actually get the name of the IBOutlet of the subview and compare it if it only starts with the word "view_"
Thanks

Xcode: iPad keyboard troubles (not that simple)

I am making an app which uses many Textfields. Most of them are inside static tableViews. I use the split view application template. Every category selected from the left panel presents a storyboard scene inside a second view on the right panel.
I just want to get rid of the keyboard with the "done" button however everything i have tried that would work on a simple view fails to work under these circumstances.
Can you please help me out with this?
p.s. I try to dismiss the keyboard inside the implementation file of the presented storyboard scene. Should i do something inside the Detail Scene of the split view controller?
Here is my Scene's code:
.h
#import <UIKit/UIKit.h>
#interface AfoEsoda : UITableViewController <UITextFieldDelegate>{
}
#property (strong, nonatomic) IBOutlet UITextField *merismataTF;
-(IBAction)hideKeyboard:(id)sender;
#end
.m
#synthesize merismataTF;
- (void)viewDidLoad
{
[super viewDidLoad];
merismataTF.delegate=self ;
}
//---------Hide Keyboard-------------------
//Tried but didn't work
-(IBAction)hideKeyboard:(id)sender {
[merismataTF resignFirstResponder];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
//Of course i do not use both methods at the same time.
EDIT:
When i set the textfield's delegate to self i get this crash:
Try implementing the textField's delegate, set the delegate to self, and in the delegate's method
- (BOOL)textFieldShouldReturn:(UITextField *)textField
set
[textField resignFirstResponder];
Another way could be going through all of the view's subviews and if it is a text field, resign first responder:
for(int i=0;i<self.view.subviews.count;i++)
{
if([[self.view.subviews objectAtIndex:i] isKindOfClass:[UITextField class]])
{
if([[self.view.subviews objectAtIndex:i] isFirstResponder])
[[self.view.subviews objectAtIndex:i] resignFirstResponder];
}}
OK, I got it. Use this with with the textFieldShouldReturn method.
So here is your answer: You have declared your text field as a property and then use alloc and init it over and over again for each cell. Probably it only works properly for the last row.
Here is an example of how your cellForRow method should look like:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ static NSString *cellIdentifier = #"My cell identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITextField *newTextField;
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
newTextField = [[UITextField alloc] initWithFrame:CGRectMake:(0,0,25,25)];
newTextField.tag = 1;
newTextField.delegate = self;
[cell.contentView addSubview:newTextField];
}
else
newTextField = (UITextField *)[cell.contentView viewWithTag:1];
And then, if you need the textField's value for a certaing row, simply use:
UITextField *someTextField = (UITextField *)[[tableView cellForRowAtIndexPath:indexPath].contentView viewWithTag:1];
NSLog(#"textField.text = %#", someTextField.text);

ARC and Popovers and delegates

i am tearing my hair out, I have migrated my old project to arc and I'm getting this error popping up : * Terminating app due to uncaught exception 'NSGenericException', reason: '-[UIPopoverController dealloc] reached while popover is still visible.'
I have read through some threads and I'm confused, some say when using delegates use a weak reference but on the other hand when using popovers use a strong property reference, can someone give me an example of how best to use ARC and delegates with a popover that has a button inside that changes the background colour for example?
From what I've read I keep hearing use an instance variable in my view controller, here it is in my main view controller:
#property (nonatomic, strong) UIPopoverController *currentPopover;
and the is the method implementation in the main view controller file:
- (IBAction)ShowPopTextColor:(id)sender {
if (currentPopover == nil) {
TimerTextColor *timerTextColor = [[TimerTextColor alloc]init];
timerTextColor.delegate =self;
UIPopoverController *pop = [[UIPopoverController alloc]initWithContentViewController:timerTextColor];
[pop setDelegate:self];
[pop setPopoverContentSize:CGSizeMake(320, 240)];
[pop presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
//[pop release];
} else {
[currentPopover dismissPopoverAnimated:YES];
currentPopover = nil;
}
}
here is my popup content header:
#protocol colorChooserDelegate
-(void) colorSelected:(UIColor*)thecolor;
#end
#interface TimerTextColor : UIViewController{
id<colorChooserDelegate> delegate;
IBOutlet UIButton *colorView;
}
- (IBAction)buttonTapped:(id) sender;
#property (nonatomic,strong) id<colorChooserDelegate>delegate;
#property (nonatomic,strong) UIButton *colorView;
#end
What am i doing wrong?
Assign currentPopover.
Call
currentPopover = pop
after popover creation
you shouldn't create a local variable to store the popover controller.
Change this
UIPopoverController *pop = [[UIPopoverController alloc] initWithContentViewController:timerTextColor];
to
self.currentPopover = [[UIPopoverController alloc] initWithContentViewController:timerTextColor];

Multiple Views for UITabBarController

I'm trying to build an app where I have a TabBarController with 4 entries.
When I select the first entry, a view with a UITableView shows up.
This TableView is filled with several entries.
What I would like to do is:
When an entry out of that UITableView gets selected, another view should show up; a detailview.
.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
if(self.secondView == nil) {
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondView" bundle:[NSBundle mainBundle]];
self.secondView = secondViewController;
[secondViewController release];
}
// Setup the animation
[self.navigationController pushViewController:self.secondView animated:YES];
}
}
.h
#import <UIKit/UIKit.h>
#import "SecondViewController.h"
#interface FirstViewController : UIViewController {
SecondViewController *secondView;
NSMutableArray *myData;
}
#property (nonatomic, retain) SecondViewController *secondView;
#property (nonatomic, copy, readwrite) NSMutableArray* myData;
#end
This is what I have so far.
Unfortunately.. the code runs, bit the second view does not show up.
Is your first view controller wrapped in a UINavigationController? When you set up your UITabBarController, you should add UINavigationControllers rather than your UIViewController subclasses, e.g.:
FirstViewController *viewControl1 = [[FirstViewController alloc] init];
UINavigationController *navControl1 = [[UINavigationController alloc] initWithRootViewController:viewControl1];
UITabBarController *tabControl = [[UITabBarController alloc] init];
tabControl.viewControllers = [NSArray arrayWithObjects:navControl1, <etc>, nil];
//release everything except tabControl
Also, based on your code, you don't need to keep your secondViewController as an ivar, since UINavigationController automatically retains its view controllers (and retaining it when you're not displaying it will use up unnecessary memory).

Resources