I have a UIPickerView that is populated by an NSMutableArray called sectionNamesArray. When an item is added to sectionNamesArray, how do I manually get this delegate to be called?
-- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
return ([sectionNamesArray count] + 1);
}
The problem is that I add this pickerView to the center of a navBar in the titleView. If I try to nil the UIPickerView and reallocate it in order to hopefully get the delegate called again, the PickerView shows up on the left side of the navbar, instead of the center. I am guessing maybe the interface builder settings got it in the center...?
Here's the code for that:
-(void) setupPickerView{
myPickerView = nil;
myPickerView = [[UIPickerView alloc] init];
myPickerView.backgroundColor = [UIColor clearColor];
myPickerView.delegate = self;
myPickerView.dataSource = self;
myPickerView.showsSelectionIndicator = YES;
[self.navigationController.navigationBar setBackgroundView:myPickerView];
CGSize pickerSize = [myPickerView sizeThatFits:CGSizeZero];
UIView *pickerTransformView = [[UIView alloc] initWithFrame:CGRectMake(-40.0, -43.0, pickerSize.width, pickerSize.height)];
pickerTransformView.transform = CGAffineTransformMakeScale(.55f, .55f);
[pickerTransformView addSubview:myPickerView];
[self.navigationController.navigationBar setBackgroundView:pickerTransformView];
}
When you add something to your array can't you use [UIPickerView reloadAllComponents] or - (void)reloadComponent:(NSInteger)component?
That will most likely query your delegate and call everything necessary to check your array and reload the view.
Related
All,
I have a UITableView w/detail, with a Search bar, created from code. Works fine on selected items except it doesn't work when an item is clicked from the search results; no delegate methods fire. Never seen this type of behavior before so I don't know where the issue lies.
I get the same behavior with standard table cells as well as custom table cells.
Appreciate any guidance on this and thanks.
Just Hangs Here
//ViewController.h
#interface SongsViewController : UITableViewController <UISearchBarDelegate,UISearchDisplayDelegate>
{
NSMutableArray *searchData;
UISearchBar *searchBar;
UISearchDisplayController *searchDisplayController;
UITableViewCell *customSongCell;
}
//ViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 88)];
[searchBar setShowsScopeBar:YES];
[searchBar setScopeButtonTitles:[[NSArray alloc] initWithObjects:#"Title",#"Style", nil]];
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
searchDisplayController.delegate = self;
searchDisplayController.searchResultsDataSource = self;
self.tableView.tableHeaderView = searchBar;
//Load custom table cell
UINib *songCellNib = [UINib nibWithNibName:#"SongItem" bundle:nil];
//Register this nib, which contains the cell
[[self tableView] registerNib:songCellNib forCellReuseIdentifier:#"SongItemCell"];
// create a filtered list that will contain products for the search results table.
SongStore *ps = [SongStore defaultStore];
NSArray *songObjects = [ps allSongs];
self.filteredListContent = [NSMutableArray arrayWithCapacity: [songObjects count]];
self.masterSongArr = [[NSMutableArray alloc] initWithArray:[ps allSongs]];
[self refreshData];
[self.tableView reloadData];
self.tableView.scrollEnabled = YES;
}
I fixed this issue. I forgot to set this searchDisplayController delegates:
[searchDisplayController setSearchResultsDelegate:self];
So make sure you set All of these:
searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:searchBar contentsController:self];
[searchDisplayController setDelegate:self];
[searchDisplayController setSearchResultsDataSource:self];
[searchDisplayController setSearchResultsDelegate:self];
This is what I tried. Nothing appears on the screen and none of the UITableView methods that you are supposed to implement are getting called.
-(void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
self.view = view;
[view release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
UITableViewController *TVC = [[[UITableViewController alloc]
initWithStyle:UITableViewStylePlain] autorelease];
CGRect cgRct = CGRectMake(0, 10, 320, 100);
UIView *newView = [[UIView alloc] initWithFrame:cgRct];
TVC.view = newView;
[newView release];
[self.view addSubview:TVC.view];
}
I've looked for good examples and tutorials on doing this programmatically but there are none.
What I am trying to achieve is a Table that doenst take up my who screen. Maybe 3/4 of my screen would be good.
Many Thanks
Code
The problem is that you're creating a UITableViewController, which is a UIViewController, and will expect to be on the nav stack. What you want to do is create a UITableView, which is a UIView. You are also not setting the table's delegate and data source, which you will need to do to get calbacks.
Your viewDidLoad should look something like this
- (void)viewDidLoad
{
[super viewDidLoad];
UITableView *table = [[[UITableView alloc] initWithStyle:UITableViewStylePlain] autorelease];
table.dataSource = self;
table.delegate = self;
table.frame = CGRectMake(0, 10, 320, 100);
[self.view addSubview:table];
}
(Note that if you're going to need access to the table outside of the callbacks, you should save it in an ivar rather than declaring it locally, and should retain it. Let me know if you need a few more lines of code to show you what I mean)
Make sure you set the delegate of TVC, with
TVC.delegate = self;
That's the reason why none of those methods are getting called. Also, make sure your class implements the UITableViewDelegate protocol by changing your interface declaration to
#interface YourViewController : UIViewController <UITableViewDelegate> {
//declare variables here
}
Also, equally important, don't set TVC.view, as this already happens when you initialize the view controller. You're just setting it to a blank view, which is why you're not seeing anything.
iOS7 seems to like this way of init'ing the tableview:
//make tableview
UITableView *table = [[UITableView alloc] initWithFrame:CGRectMake(0, 81, 200, 200) style:UITableViewStylePlain];
table.dataSource = self;
table.delegate = self;
[self.dataView addSubview:table];
try that out. Hope it helps someone.
UIView *newView = [[UIView alloc] initWithFrame:cgRct];
TVC.view = newView;
I'll give you a hint. Here you are setting the view of the UITableViewController to an EMPTY VIEW...
You can scroll an UIPickerView both by swiping and by tapping items under or above the selection belt.
You can populate an UIPickerView by either specifying NSString as titles or reusable UIViews for each row.
What I've noticed is that when I switched from providing strings to providing view, tap to scroll no longer worked. Is this expected behavior. Should my views forward some touch events to the UIPickerView?
Thanks
EDIT:
Here's a code sample from my implementation:
// Creating the picker
screenPicker_ = [UIPickerView new];
screenPicker_.showsSelectionIndicator = YES;
screenPicker_.delegate = delegate_;
screenPicker_.dataSource = delegate_;
[self addSubview:screenPicker_];
[screenPicker_ release];
// Row view creation delegate
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
PickerRowView *pickerRowView = (PickerRowView*)view;
if(view == nil)
{
pickerRowView = [[PickerRowView alloc] initWithFrame:CGRectMake(0, 0, pickerView.frame.size.width, PICKER_ROW_HEIGHT)];
}
[pickerRowView SetTitle:#"some title"];
return pickerRowView;
}
// initailizer of the PickerRowView class (an UIView subclass)
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
CGFloat titleHeight = frame.size.height * CONTENT_TO_FRAME_RATIO;
title_ = [[UILabel alloc] initWithFrame:CGRectMake(TITLE_X, (frame.size.height - titleHeight) / 2, frame.size.width, titleHeight)];
[title_ setFont:[UIFont fontWithName:#"StainlessExt-Light" size:titleHeight]];
title_.backgroundColor = [UIColor clearColor];
[self addSubview:title_];
[title_ release];
self.userInteractionEnabled = NO;
}
return self;
}
Setting userInteractionEnabled property to NO for your row views must solve your problem.
In your PickerRowView class, define the following method:
- (void)didMoveToSuperview {
if ([[self superview] respondsToSelector:#selector(setShowSelection:)])
{
[[self superview] performSelector:#selector(setShowSelection:) withObject:NO];
} }
and that should get rid of the highlight
According to Apple's docs, "Subclasses need not override -[UIView drawRect:] if the subclass is a container for other views."
I have a custom UIView subclass that is indeed merely a container for other views. Yet the contained views aren't getting drawn. Here's the pertinent code that sets up the custom UIView subclass:
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Consists of both an "on" light and an "off" light. We flick between the two depending upon our state.
self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
self.onLight.backgroundColor = [UIColor clearColor];
self.onLight.on = YES;
[self addSubview:self.onLight];
self.offLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
self.offLight.backgroundColor = [UIColor clearColor];
self.offLight.on = NO;
[self addSubview:self.offLight];
self.on = NO;
}
return self;
}
When I run the code that displays this custom UIView, nothing shows up. But when I add a drawRect method...
- (void)drawRect:(CGRect)rect
{
[self.onLight drawRect:rect];
[self.offLight drawRect:rect];
}
...the subviews display. (Clearly, this isn't the right way to be doing this, not only because it's contrary to what the docs say, but because it -always- displays both subviews, completely ignoring some other code in my UIView that sets the hidden property of one of the views, it ignores the z-ordering, etc.)
Anyway, the main question: why don't my subviews display when I'm not overriding drawRect:?
Thanks!
UPDATE:
Just to make sure that the problem doesn't lie in my custom subviews, I added in a UILabel as well. So the code reads:
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Consists of both an "on" light and an "off" light. We flick between the two depending upon our state.
self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
self.onLight.backgroundColor = [UIColor clearColor];
self.onLight.on = YES;
[self addSubview:self.onLight];
self.offLight = [[[LoyaltyCardNumberView alloc] initWithFrame:frame] autorelease];
self.offLight.backgroundColor = [UIColor clearColor];
self.offLight.on = NO;
[self addSubview:self.offLight];
self.on = NO;
UILabel* xLabel = [[[UILabel alloc] initWithFrame:frame] autorelease];
xLabel.text = #"X";
[self addSubview:xLabel];
}
return self;
The "X" doesn't display either.
UPDATE 2:
Here's the code that invokes my custom UIView (OffOnLightView):
// Container for all of the OffOnLightViews...
self.stampSuperView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)] autorelease];
[self.view addSubview:self.stampSuperView];
// Draw the stamps into the 'stamp superview'.
NSInteger numberOfCardSpaces = (awardType == None) ? 3 : 10;
for (NSInteger i = 1; i <= numberOfCardSpaces; i++)
{
OffOnLightView* newNumberView = [[[OffOnLightView alloc] initWithFrame:[self frameForStampWithOrdinal:i awardType:awardType]] autorelease];
newNumberView.on = (i <= self.place.checkInCount.intValue);
newNumberView.number = [NSString stringWithFormat:#"%d", i];
[self.stampSuperView addSubview:newNumberView];
}
Your subviews should have their frame initialized to the bounds of the parent uiview. Subviews are in a different coordinate system that is relative to the frame of the parent.
self.onLight = [[[LoyaltyCardNumberView alloc] initWithFrame:self.bounds] autorelease];
You should never call -drawRect: manually. If you need to force a redraw, call -setNeedsDisplay.
I would start by debugging (breakpoint or NSLog()) in the -drawRect: methods of the two subviews you are adding to make sure they are actually performing their drawing.
Also note how you're making both subviews the full size (frame) of the containing view, and setting their background colours to clear. I'm going to guess this is intentional, but it's possible they are displaying, but you just can't see anything due to them having a transparent background.
I created a custom UITableViewCell class with a UIButton, a UIImage, and two UILabels. The button and the image are overlayed on top of each other, and only one is displayed at a time. The expected behavior is you touch on the button, the button disappears, and it displays the image. The UITableViewCells are set to be reused. Here's my code:
Constructor:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
unheartButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
unheartButton.backgroundColor = [UIColor clearColor];
unheartButton.frame = CGRectMake(10, 13, 20, 18);
[unheartButton addTarget:self action:#selector(onButtonClick:) forControlEvents:UIControlEventTouchUpInside];
[unheartButton setBackgroundImage:[UIImage imageNamed:#"redheart.png"] forState:UIControlStateNormal];
imageView = [[UIImageView alloc] init];
imageView.frame = CGRectMake(12, 13, 16, 16);
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int ndx = 1; ndx < 13; ndx++) {
[array addObject:[UIImage imageNamed:[NSString stringWithFormat:#"icon-loading-%d (dragged).tiff", ndx]]];
}
imageView.animationImages = array;
imageView.animationDuration = 1;
[array release];
[self.contentView addSubview:imageView];
[self.contentView addSubview:unheartButton];
return self;
}
}
- (void) setModel:(MyModel *)myModel {
model = myModel;
if (model.hideImage) {
imageView.hidden = YES;
unheartButton.hidden = NO;
else {
imageView.hidden = NO;
unheartButton.hidden = YES;
}
}
Button click:
- (IBAction) onButtonClick: (id) sender {
model.hideImage = NO;
unheartButton.hidden = YES;
imageView.hidden = NO;
[imageView startAnimating];
[self setNeedsDisplay];
[self.contentView setNeedsDisplay];
[self.unheartButton setNeedsDisplay];
[self.imageView setNeedsDisplay];
}
I'm calling setNeedsDisplay on everything, but nothing seems to happen. If I scroll off the screen and back up, the button is hidden and now the loading icon is shown, but this only happens after a scroll. I'm not sure what I need to do to get the cell to repaint.
The UITableView does some fancy caching to support scrolling and the like; I've had similar issues with refreshing a specific cell when I use custom views.
You can use reloadRowsAtIndexPaths:withRowAnimation: to make the table reload just that row. See this post for more information: Why won't my UITableViewCell deselect and update its text?