I have an app that has a MyViewController with two subclasses, MyViewControllerMain and MyViewControllerSettings.
In MyViewControllerSettings, I allow the user to change the font, color, etc. How do I instruct MyViewControllerMain to refresh itself so the changes show up?
Thanks,
Tony
Here is what I am doing:
I initialize it in viewDidLoad of my superclass like this:
m_fFontSize = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? 28 : 16;
m_MyFont = [UIFont fontWithName:#"Greg's Hand" size:m_fFontSize];
And then in derived settings class, I do this:
NSString * sFont = [fontList objectAtIndex:row];
m_MyFont = [UIFont fontWithName:sFont size:m_fFontSize];
And in my dervied MainView class in viewWillAppear, I do this:
tfName.font = m_MyFont;
It depends - if you have, say, a UILabel, UIButton or the like in your MyViewControllerMain and you change their font / color, they should update themselves (make sure you really set the font, color, etc!).
Otherwise, you can always try [[myViewController view] setNeedsDisplay].
For this you need to redraw the whole view using your settings.
For each & every time, method name:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//Over here just set the font size of all the controllers you are using.
This will definitely solve your problem since this method is called every time before the View Appears.
}
Related
I think this is pretty weird. So I have a UIView and I want to change the textcolor of all UILabel. Here is what I did:
for (UILabel *label in [self subviews]) { // self is the UIView
label.textColor = someColor;
}
When I run the code, it crashed with error like UIImageView: unrecognized selector setTextColor: sent to instance (some instance)
So it seems that the label in the fast enumeration is actually a UIImageView. By the way, I do have two UIImageViews in the UIView *self. However, shouldn't the fast enumeration give only UILabel only (because I specified UILabel *label instead of UIView *label)?
I think this is the problem because when I wrote the following code, it works.
for (UILabel *label in [self subviews]) { // self is the UIView
if ([label isKindOfClass:[UILabel class]]) {
label.textColor = someColor;
}
}
So in this code, when I check to guarantee that label is a UILabel and then set its textcolor, all UILabels in the view changes their color correctly.
Can someone explain why I need the if-statement to double-check the instance type?
Your 2 loops are very different. The first assumes that all subviews will be labels, the second checks each subview to ensure it is a label. The first should check too.
Fast enumeration is basically syntactic sugar, it's just a normal for loop. It doesn't magically filter or manipulate the list you give it to enumerate.
You're reading the enumeration as "loop for every UILabel in my subviews", but that's not how it really works-- fast enumeration doesn't do any smart filtering, it's just a shortcut. You should read it as "loop for every object in my subviews, which are UILabels". And because not every subview is in fact a UILabel, you have problems when trying to treat them all as one.
Every object in that array will be a UIView though, so the more telling syntax would be:
for (UIView * view in self.subviews) {
if ([view isKindOfClass:[UILabel class]]) {
UILabel * label = (UILabel *)view;
// do something with label...
}
}
in the first loop your are iterating for all subviews of the main view while in the second for loop you are still iterating for all the objects but modifying only UILabel type elements.
Your main view can contain all types of views like UIImageView UILabel etc.....Enumerating over UIlabel as a datatype doesn't actually change the datatype of it and not going to enumerate over only that type of elements..
More elegant: tag your labels.
#define kLabelOffset 100;
for (int i=kLabelOffset +1; i < totalLabels; i+) {
UILabel *label = (UILabel*) [self viewWithTag:i];
label.textColor = someColor;
}
[self subviews] returns an NSArray which contains (id) pointers to UIViews. These could be any type of View including UILabels. So if a particular subview does not support setTextColor you will get an unrecognised selector message. So you do need your isKindOfClass test or you could use a respondsToSelector test to be more general.
In objective C we do not have type casting. Just by using fast enumeration and assigning that variable to UILabel doesnot typecast it to UILabel. Thats the reason for the crash. We can either use respondsToSelector or as you have used isKindOfClass to make the code work as expected.
My question is kind of general; in my project I have 10 UIButton objects
named Button1, Button2, Button3, Button4, Button5, Button6, Button7, Button8, Button9, and Button10
I also have UIImageview That are named exactly like the buttons from 1 to 10.
I want to write a code that will manipulate the image by the last character of the button (always a number from 1 to 10) and will affect the UIImageview the same way
Something like this
buttonlastcharacter = i;
if(sender.lastcharacternumber is:i){
Button%,i.frame = //Some manipulation
But basically all that I want is to have access to a certain object by string
How can I implement such a behavior?
There are a couple of better ways to do this. If these buttons are all static and in IB you can use an IBCollection array for image views and buttons to simply call them up by matching indexes.
Better yet just use the tag value for the buttons or image views.
It is maybe not the ideal solution in your case, but you can do it different ways:
using kvo
UIButton* myButton = [self valueForKey:[NSString stringWithFormat:#"button%i",i]];
or with selectors and properties
UIButton* myButton = [self performSelector:NSSelectorFromString([NSString stringWithFormat:#"button%i",i])];
Hm, you could use an array for your buttons and a tag for your UIImageView objects. They all inherit from UIView, which provides you with a .tag propterty. It is of type NSInteger* .
For convenience reasons I would suggest to name the buttons from 0 to 9. It does not really matter but the first index in the array would be 0 and therefore naming them accordingly just makes things easier.
Define
NSArray *buttonArray;
You may opt for NSMutableArray depending what else you may want to do with it.
In viewDidLoad code:
buttonArray = [NSArray arrayWithCapacity:10];
buttonArray[0] = button0;
..
buttonArray[9] = button9;
In your XIB file in Interface Builder, or whereever you may create the UIImages programmatically, add the tags accordingly.
image0.tag = 0;
...
image0.tag = 9;
assuming you name them image0 to image9.
In your appropriate action method code:
buttonArray[sender.tag] = someManipulation;
You can do it like this,
In your IBAction method:
- (IBAction)click:(id)sender
{
UIButton *but = (UIButton *)sender;
but.frame = your manipulation code;
}
or you can check the title like:
if([but.currentTitle isEqualToString:#"Button1])
{
//Manipulate button 1
}
if you have added tags for the buttons from 1-10 you can use,
if(but.tag == 2)
{
//Manipulate button 2
}
I ran into difficulties with SSCollectionView and SSCollectionViewItem.
First of all I'd like to get it initialized from IB. But that won't work for me.
I have a SelectFooViewController which is:
#interface SelectFooViewController : SSCollectionViewController { ... }
and am using it as filesOwner of the corresponding XIB.
SelectFooViewController* selectFooVC = [[SelectFooViewController alloc]
initWithNibName:#"SelectFooViewController" bundle:nil];
But since it wont work I had to initialize its properties inside viewDidLoad() myself.
Furthermore I am not able to display anything except the backgroundColor of my SSCollectionViewItems. What I want is a textLabel and an image .
- (SSCollectionViewItem *)collectionView:(SSCollectionView *)aCollectionView itemForIndexPath:(NSIndexPath *)indexPath {
SSCollectionViewItem *item = [[[SSCollectionViewItem alloc] initWithStyle:SSCollectionViewItemStyleImage reuseIdentifier:itemIdentifier] autorelease];
SSLabel* label = [[SSLabel alloc] init];
[label setText:#"foo"];
item.imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"foo.png"]];
item.textLabel = label;
[label autorelease];
return item;
}
I can confirm that the delegate methods (for determining the number Of rows, sections and such) are implemented and working as expected. But my items are all empty - but react onclick with the expected popup.
Does anyone see an error in what I did? - Thanks...
EDIT: I was also not able to display a local image by changing SSCatalog project
I just figured out, that I have to set the frame of each property (textLabel, detailTextLabel and imageView) myself. That fixed it.
When you create instance SelectFooViewController just insert this line
selectFooVC.view;
or
selectFooVC.view.hidden = NO;
And then add it to the view.
This is because the view is not initalised until you explicitly access it. Hence your items are loaded only when you click it and not immediately. You can call it a hack but i don't call it one. :-)
I have 5 different views and when I tap the button, I want to push one of 5 views randomly. However, I do not want to make 5 different controllers for each views. Do I have a chance to put them in one controller? If so, how?
You can have as many views as you want in a single UIViewController subclass. You can create them all in Interface Builder as well, in the one .xib file for your UIViewController class (it might get a bit hard to see, though, better to lay out each UIView in its own .xib). You can present them in any combination you want. Assuming you want to just show one view at a time, you can do this:
In your viewDidLoad method, start out showing the initial view, and in your class keep track of which is the current view:
...
#property(nonatomic, retain) UIView *currentView;
...
- (void)viewDidLoad
{
[self.view addSubview:self.defaultView];
self.currentView = self.defaultView;
}
Then to switch to a particular other view do this:
- (void) switchToView:(UIView *)newView
{
[self.currentView removeFromSuperView];
[self.view addSubview:newView];
self.currentView = newView;
}
}
Or you can show them all at one time: just [self.view addSubview:theView];
You have to set the tags of all the views, and then use
int r = arc4random() % 5;
method to find the random number. Use this newly generated number to check the tag, in your selector
Just change the view outlet of the controller on the action of the button. Use random numbers to select which view. Ouh and - (void)setNeedsDisplay :)
When I programmatically allocated a UILabel in my custom initWithNibName method, and later in viewDidLoad, tried to assign a string to it, the label was not pointing to anything. I didn't release it; the label shows on the screen. If I create the label in IB, and assign text to it in viewDidLoad, it works.
Is it against a rule to set up manually allocated objects in viewDidLoad?
Why is it not pointing to anything, even though viewDidLoad is called after my init?
From the doc of viewDidLoad:
This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method. This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
In my init:
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)];
_descriptionLabel.numberOfLines = 0;
_descriptionLabel.lineBreakMode = UILineBreakModeWordWrap;
_descriptionLabel.font = [UIFont systemFontOfSize:12.0];
_descriptionLabel.adjustsFontSizeToFitWidth = NO;
_descriptionLabel.text = #"Description not found.";
_descriptionLabel.backgroundColor = [UIColor clearColor];
In viewDidLoad, the variable's value is 0x0.
It's the same with my manually allocated UIButton, which is fully working once the view loads.
If you want to create the UILabel programatically you can, but you still do it in viewDidLoad (as opposed to initWithNibName).
Don't be afraid to do UI setup in viewDidLoad. It is provided to add any static UI elements BEFORE the view appears on screen.
The view will not appear until just before viewDidAppear:(BOOL)animated is called.
If you have dynamic content configure it in viewWillAppear:(BOOL)animated. (This looks like your situation)
Then make sure you add it to the view:
[self.view addSubview:myLabel];
If you need future access to your new label, you will need to create an ivar to hold a pointer to it.
In the code posted, the _descriptionLabel UILabel is not retained and will be released before the view is drawn.
Try
_descriptionLabel = [[[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)] retain];
Make sure you put [_descriptionLabel release] in dealloc. This assumes _descriptionLabel is an instance variable.
This basically applies to any object you create with alloc, copy, or new.