So, I'm trying to change the UILabel on a UIButton to an instance of an FXLabel instead. Check out the link for more on FXLabel (https://github.com/nicklockwood/FXLabel). I know by using class extensions (Subclass UIButton to add a property), I could always add another property, in my case, an FXLabel by just adding a subview basically. However, I like the convenience and features of the titleLabel property already present.
Is there some method to "switch" the UILabel for an FXLabel by subclass or category?
I do know I could do this, but it's such a hack and isn't future proof. And I don't know how to then change the class type (Get UILabel out of UIButton).
UILabel *label;
for (NSObject *view in button.subviews) {
if ([view isKindOfClass:[UILabel class]]) {
label = (UILabel *)view;
break;
}
}
Thoughts?? Thanks.
Here's another way you could at least access the label. Create a subclass of UIButton, and in that subclass override layoutSubviews. From there you can access the label directly:
- (void)layoutSubviews {
[super layoutSubviews];
UILabel *titleLabel = [self titleLabel];
}
Now we're still stuck with the problem of changing that label to be a subclass. I guess you could try to dynamically swap the UILabel's class with your FXLabel subclass (as detailed here), but to be honest that's pretty risky.
I'd say you're better off just creating your own UIControl that you can customize to your hearts content. No risk, just pure goodness. It shouldn't be too hard to imitate a simple UIButton. Good luck!
Sorry to say it but I don't think you can do this in any reliable way. Even if you subclassed UIButton and overrode the titleLabel methods you'd also have to handle the state settings. And you never know when the internal code refers directly to a private ivar for the label.
I created a subclass of UIButton for my apps to do something similar (custom button layouts & subviews), sorry I can't share it right now, it's owned by a client. It's not too difficult to create your own state handling code for your own subviews.
Related
Is there an easy way for listening for when a child or subview has been added to a UIView?
I've gone through the addobserver options and haven't found an obvious option anyway. There may be another option that would be affected though when content is added to a view or am I wrong in saying that? i.e. content width or height, positions, etc.?
Edit
This is accomplished easily using the advice of #Alkimake below (TextHolderClass).
I created a custom UIView subclass and set the UIView's class in Interface Builder to be equal to TextHolderClass
Thanks for your help, I know it should have been obvious :)
UIView methods may help you:
- (void)willMoveToSuperview:(UIView *)newSuperview
UIView has 2 methods to call after subview interactions. Simply create your custom UIView class and implement these methods which is pretty for you. And use your own CustomView
- (void)didAddSubview:(UIView *)subview;
- (void)willRemoveSubview:(UIView *)subview;
Basically, I cannot figure out how to change the background image. I have searched and searched and just cannot seem to find it. Anyone have any ideas? Thanks!
Update
Here is the code I use to show the View:
SettingsViewController *settingsView = [[SettingsViewController alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:settingsView animated:YES];
If anyone needs anything else to help out I'll do my best! Thanks!
Passing a messages between different views can be done by direct call of the methods (not a good architectural solution but maybe not critical for a small projects) or with an event driven model as described in an answer above.
As for binding of a collection of objects and processing the afterward I recommend to review IBOutletCollection keyword that allows to bind multiple objects fro an InterfaceBuilder to a property with a type like NSArray.
Property declaration will look like following:
#property (nonatomic, retain) IBOutletCollection(UIButton) NSArray *buttons;
The code to change the background for all the buttons will look like following:
UIImage *backgroundImage = [UIImage imageNamed:#"background.png"];
for(UIButton *button in buttons) {
[button setBackgroundImage:backgroundImage forState:UIControlStateNormal];
}
You use setBackgroundImage:forState: to set the image.
Regarding the different views part, it depends on how you wrote your code. If the view with the buttons is controlled by the different view (by creating an instance and using addSubview:) then you can call it directly by using instanceName.buttonName (as long as you declare it as a property -thanks fichek).
If you don't manually add the view, instead through IB, you can have the button that controls the other button's image point to the IBAction in that class.
If neither of those options work you can always use NSNotificationCenter.
Typically when creating an action from something like a button you have this
[button addTarget:self action:#selector(method:)...
I can guess if I want to send the action to another class but what if I want to send it to the parent? I thought super might work but that gives a warning.
EDIT:And super crashes the App
Cheers for any help.
Let's assume you have the following classes:
#interface Base {
}
- (IBAction)method:(id)sender;
#end
#interface Derived {
}
#end
If Derived doesn't have it's own implementation of method: then your standard setup with self and #selector(method:) will do what you want.
If Derived DOES have it's own implementation of method: then you have no choice but to add your own method in derived that doesn't nothing but forward the call up to the superclass implementation.
- (void)callMethodOnSuper:(id)sender {
[super method:sender];
}
Then use #selector(callMethodOnSuper:) instead. You cannot just supply super as the target.
EDIT: The following is an update based on the comment below actually clarifying what the OP wants to do.
Just to make sure I'm clear, I'll restate what your seems to be your situation. You have a view controller, say MyViewContoller, whose view has three subviews. One of those subviews, a MyCustomView, has some UIButton subviews.
My suggestion is as follows:
#interface MyViewController {
}
#end
#interface MyCustomView {
UIButton* button1;
UIButton* button2;
}
#property (nonatomic,readonly,retain) UIButton* button1;
#property (nonatomic,readonly,retain) UIButton* button2;
#end
When your view controller is building it's view:
[myCustomView.button1 addTarget:self action:#selector(method:)...
[myCustomView.button1 addTarget:self action:#selector(method:)...
The way I understand your question, you are trying to implement something that does exactly the same as the delegate pattern, but the way you are going around it you will not get any compiler hints or error if the super class does not implement the correct method for the target, it will simply crash when you run it.
It might seem as a good idea in regards of encapsulation, but I think it would be hard to debug if you reuse the view component elsewhere.
The only case I use the approach of trying to message something other than "self" is where a ViewController has X views and these views needs each other and needs to react to each others actions. Still, if I have implemented a viewController as delegate for the views, I would just let the viewController change the other views to whatever state they should go to.
(hope I didn't misunderstand the question)
I need to generate a custom button through code, this is how i am currently doing it.
-(void) initialiseButtons
{
int ypos = playerImage.frame.origin.y + playerImage.frame.size.height + 8;
for(int i=0; i<totalButtons; i++)
{
UIButton *newButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[newButton setFrame:CGRectMake(20, ypos, 220, 30)];
newButton.tag = 10 + i;
[newButton addTarget:self action:#selector(statTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.frontView addSubview:newButton];
ypos += 30 + 7;
}
}
This creates the blank buttons perfectly through code, gives them a tag and assigns an callback function on touchUpInside.
The custom button needs to be able to handle showing an image when pressed down.
It needs to be able to draw 2 pieces of Text. 1 aligned to the left hand side of the button and 1 aligned to the righthand side of the button.
My boss suggested instead of buttons I use a View. I dont understand how this will work. When i start thinking about it, i think it would require having a viewcontroller dedicated to the buttons. And some draw method? It sounds complicated and I am not grasping how it can be done.
Is there a simpler method by making a custom class overriding UIButton? I made a simple test class earlier but nothing was drawn in the buttons locations when I used them in place of the Normal UIButton class. I expected i would see the buttonUp.png drawn.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface CardButton : UIButton {
}
#end
#import "CardButton.h"
#import <UIKit/UIKit.h>
#implementation CardButton
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self setImage:[UIImage imageNamed:#"buttonUp.png"] forStates:UIControlStateNormal];
self.adjustsImageWhenHighlighted = YES;
}
return self;
}
#end
Anybody able to give me some pointers on this? I'm pretty blocked at the moment. Have been reading some various other threads related to buttons but nothing that made it clear in my head how to tackle the problem
Personally, I would subclass UIButton. Despite all the talk that you can't subclass UIButton, Apple even talks about subclassing UIButton in the UIButton documentation.
In the subclass I would create a UIView with the two labels (or images or whatever) and add them as subviews to the button (be sure to set interactive for the text and view as FALSE).
What is awesome about this is that it leverages the UIButton code and keeps you from reinventing the wheel.
You should not (I'd almost say you can't) subclass UIButton, as it turns out to be a class cluster, and it would be impractical (read: impossible) to derive from that.
So your two options are:
1) Use standard UIButton of custom type, add the elements you want to show (i.e. UILabel) and hook up actions for touch down and touch up and react accordingly (change the views, trigger actions etc.)
2) Use a custom UIView, implement drawRect: to draw how you like it or use custom views as subviews as in 1). Then use the touchesBegan:, touchesEnded: etc. messages to react accordingly (or UIGestureRecognizer if you can live with 3.2+ compatibility).
You might build a factory to build those buttons if they all are very similar, so that your code becomes clean and non-repetive.
Edit as per iOS 6:
Apple now mentions subclassing in the UIButton docs, so while still not very well defined, it seems quite safe now to do so.
An easy way to do this, is to override a UIView. In the view you add a UIButton as Subview. This way you have a reusable class without the need to re-implement button behaviour.
You can style your button the way you want in the initWithFrame method of your UIView derived class. If you use this class as target for your button events, you can implement special behaviour easily, like showing the image.
For the two pieces of text, you create two labels and add them as subviews to the button.
I've been following a tutorial that manipulates a uitableview from a uiviewcontroller to generate nicely styled cells.
I was wondering is it possible to do same to a class that subclasses uitablewcontroller instead of uiviewcontroller. The user uses code like:
tableView.rowHeight = 50;
tableView.backgroundColor = [UIColor clearColor];
In the viewdidload method. I would like to do the same but again in a uitableview sub class. I tried to do
self.rowHeight = 50;
But this didn't work. Does anyone know how I can implement this?
Thanks a million!
This is the actual tutorial site: http://blog.atrexis.com/index.cfm/2009/1/6/iPhone--Customize-the-UITableCellView
The Cocoa design patterns encourage using a controller object to configure views, but there's no reason why you can't subclass like you're describing, especially if you need to add functionality that can't be done any other way. You can use properties and methods just how you're describing, so there's a problem somewhere else. What method are you subclassing to assign the row height? Have you checked in the debugger to make sure your subclass is being allocated instead of a regular table view?
A UITableViewController IS a subclass of UIViewController.
The only diffference is that it conforms to the UITableViewDelegateDataSource and UITableViewDelegate protocols and it has an additional instance variable: tablview.
In viewDidLoad, you can set any table properties you like except for style (style has to be set in the initializer or in IB)
- (void)viewDidLoad{
[[self tableView] setRowHeight:kCellHeight];
[[self tableView] setTableHeaderView:myView];
//etc...
}