Setting the text on UIButtonLabel on a PLUICameraViewController - objective-c

I'm trying to set the text of the "Retake" and "Use" buttons on the PLUICameraViewController. I've programmatically navigated through the view hierarchy and found a few UIButtonLabel objects. Logging the text of these shows what I expect: "Retake", "Cancel" etc. However setting the text doesn't work. The buttons just keep their original text. Here's what I've tried:
if (q is a subclass of UIButtonLabel)
if ([q respondsToSelector:#selector(setText:)])
[q performSelector:#selector(setText:) withObject:#"zzz"];
The code runs w/o crashing. Also respondsToSelector is returning YES. How can I make the text on the buttons update?
By the way I'm aware of other approaches involving custom buttons, my own views etc. I'm really curious from an Objective-C/runtime/iOS perspective why the above code doesn't work.
Also note I'm doing this in
navigationController:willShowViewController:viewController:animated:

In this case, I don't think it's an issue of Private APIs, per se. This is just the way UIButton works.
Yes, the title of the button is a UILabel, and in practice, it is probably/currently an instance of the private class UIButtonLabel. But, the way Apple intends for you to change the title text is to use the methods in UIButton itself:
[self.button setTitle: #"newTitle" forState: UIControlStateNormal];
[self.button setTitle: #"newTitle" forState: UIControlStateSelected];
instead of trying to drill down into the button's subview heirarchy, find a button label, and call setText:.
Here's the relevant API documentation
As to why it doesn't work, I suppose that's because Apple wants it that way.
For one, changing a button's title is expected to be a normal use case. So, for convenience, they probably wanted to give you an easy method to call, directly in the UIButton interface. If they forced you do drill down into the view hierarchy, not only would you have to write more code, but it makes it a little harder for Apple to change the underlying implementation of a button. They may want to preserve the ability to change it later, and for that, it's better for them to keep some sort of wrapper APIs at the UIButton level.
Also, if you directly change the text on the button label, you are circumventing their design, where the label text depends on the button state (e.g. normal, highlighted, selected, etc.) Even though most people probably use the same button text for all states, the design allows for state-dependent text, and therefore, hiding the underlying UIButtonLabel helps enforce this design.
That's my guess as to their motivation.
So, to enforce this, it's entirely possible that in their implementation of UIButtonLabel setText:, they decline to update the text after the button has been initialized. After that, you have to use the setTitle:forState: method.

Related

Xcode-style font picker

I'd like to add a font picker in my app. Xcode's Interface Builder has a great implementation which is used in Xcode's own preferences' window too.
It's the one with a little 'T' button (seemingly) inside the font name text field itself.
Is this a standard cocoa implementation?
You'll need to roll your own. See Subclassing NSControl. You'll subclass NSTextField and NSTextFieldCell. Read the entire guide, actually. Once you've got a good understanding, then you can override the drawing/geometry routines to return a rect whose width is slightly smaller (small enough to leave room for the font "button"). Then draw your font button. You'll respond to its mouse events on the font button the same as you would for any NSView.
There's no standard control for the preview text box with the button, but there is the standard NSFontPanel class that you can use to let users select the font once the 'T' button has been clicked.
Best idea is probably to override NSTextFieldCell. I've been able to get the desired appearance by overriding -drawInteriorWithFrame:inView:, and passing a slightly less wide frame when calling through to super, then drawing the button myself. You'd also have to implement the mouse tracking of the button yourself, but you could probably just create your own NSButtonCell instance and call through to it for a few methods with the sub-rect for the button.
For convenience, you may also want to create a NSTextField subclass that uses this cell instead of a straight NSTextFieldCell, but if you're loading stuff from a XIB, you can just change the class of the cell in the XIB and leave it in a regular text field view.

How to add a second UILabel to a UIButton, configurable per UIControlState

I want to display 2 strings (at different positions in the button) with different fonts and colors (I'm using the button's setTitle for one, and I need another one), and some attributes must be changed based on the current UIControlState (like the color).
So, I'm searching the best way to add a second label to a UIButton.
I need the label to be configurable per UIControlState (I want a different color for UIControlStateNormal and UIControleStateHighlighted for example).
I've tried the following approches:
Subclass a UIButton and use drawRect: while not recommended (and I now understand why), I don't think it's even possible, it looks like the button's drawRect method is called (and after the one of my subclass) even if I don't call super.
Create a new UILabel and add it as a subview to my button: this is working quite well, except I don't know how to change the color when the UIControlState of the button is changing
Create a new layer and use drawLayer: I don't know how to get the drawLayer method to be called every time the button state is changing (my drawLayer only gets called once, when I use setNeedsDisplay just after adding my layer to the button)
Is there another way to achieve what I'm trying to do, or maybe one of those solutions might work (with a few tweaks)?
Thanks!
The second of your approach works fine. Just add 2 targets: First update to "normal state" target using "all touch events". Second update to "highlighted" using "touch down" event.
If the states are not only changed by touches and want to handle this more generally, Id suggest multithreading. All you really need is calling performSelectorInBackground when initializing all this elements (the selector updates label according to button state) and then again call same performSelectorInBackground on the end of "update label" method, creating an infinite loop.
Ok, I think I found a working solution (for my problem at least).
I'm subclassing the UIButton class (it works for me, since I'm using a custom drawn button anyway), and I override the titleRectForContentRect method that gets called everytime the title has to be displayed (including after a state change, just before display).
I added an UILabel to the button's view to display the second string I want, and during the titleRectForContentRect, I compute the correct frame location for my label, I update my label's text font and color based on the button's state (self.state), and that's all I need.

UIButton category/subclass drag and drop iPad

I don't know what's the best way to deal with this problem:
I have some draggable buttons in an app that can be moved on top of some targets. If the button is on top of the right target, let's say button1 is on top of target1, the player/user gets a point. I thought the best way to deal with this would be to make a Target class with an "identifier" attribute and a Draggable class with a "targetID" attribute. When the Draggable object is released, I check whether it's on top of the right Target object by comparing their attributes.
It sounds easy but it hasn't been. I tried first to make Draggable a subclass of UIButton. As you might already know, that is not a good idea since UIButton returns an instance of a different class when [UIButton buttonWithType:] is called, eg. UIButtonTypeRoundedRect is returned. I can't use a category, since what I need is to have an extra attribute and as far a as I know categories can't add attributes to a class, only methods. Composition would make everything very complicated since I would have to "listen" for the button actions from the controller of Draggable. I will choose this alternative, though, if I don't come with a smarter option.
I might be overseeing something very obvious or I might don't know something absolutely basic. In that case, sorry! My iOS knowledge IS very basic.
UIButton is a kind of UIView, which has a tag property that you can use to mean anything you want. It's an NSInteger, but that should be useable for what you're trying to do.

Add a second image to a custom UIButton

I've got an Custom UIButton. It's got
a "static" background image
a variable text (the Title) which gets set in the code
Now I would like to add an icon (UIImage/UIImageView) inside the button on the left of the text. (I use the indent to move the text slightly to the right). Is there an easy way of adding that icon (and referencing it from code, so I can change it) or would you recommend creating a completely new button e.g. based on a UIView? (e.g. a view, that responds to touches)?
I'm just trying to get a feel for what the best approach would be for this. Any experience?
Two ways:
I prefer doing this by subclassing a UIView and (as you mention) implement the UITouch-responder methods. You're also able to override drawRect: what I really like because it makes your app soooooo much faster!
Since UIButton is a subclass of UIView, you can add a UIImageView to your button by using addSubview:. If you save it in your main-class (#property (...) UIButton *button) you can always access it by calling [self button]. You can also subclass your UIButton and add this #property. That's up to you.
It's totally up to you. However I prefer the first way!

UILabel inside a UIToolbar using IB is invisible on run, how fix?

I want to show a total inside a toolbar. I put in IB the UILabel on top of the toolbar .
However, when I run the app, the UILabel is totally invisible (but can set values on code fine).
The most bizarre thing is that in other form, all work fine. I don't see why in one form work but not in another...
Any idea in how fix this? Or why is this behaviour happening?
Don't use a UILabel.
Use a UIBarButtonItem. Then set it to style: plain. It looks like a label, but it actually borderless button. This is the general practice of displaying text on a bar.
You can also create UIBarButtonItem with a custom view in code. You are simple "wrapping" the UILabel in a UIBarButtonItem allowing you to add anything you want to a tool bar.
To add in response to comment:
Either way, you make the button "inactive" and it doesn't respond to touches. Even though it is a button, it doesn't appear to be one. This is how Apple expects to add views to a toolbar/navbar as apposed to "float things on top of it". It violates no HIG guidelines, much the opposite, it is a reccomended design technique.
To stop the glow:
Create the button programmatically, make sure it is disabled, add it to the bar, it should then be disabled, but not dim.
In IB, have you tried to select the label and use the "Bring to Font" menu item (under Layout)? It seems like you are trying to do something pretty standard.
When you try to set values, is the label coming up as nil or at address 0x0? It's possible that the label is there, but its text cannot be set because its instance is faulty (not properly connected in IB to the IBOutlet).... Just put a breakpoint on the line where you are trying to set the value(s) for the label, and verify that the label variable is not nil (or 0x0). If it's not, try setting the text and verify on the next line that its text was set properly.
drag a UIButton into your UIToolBar. Then uncheck User Interaction Enables for this button.
Customize your UIButton so that it will look like a UILabel. Hope this will help you.