Problem setting property value on Custom Control Template Part - silverlight-3.0

I have a custom control template that contains a Slider control.
I name that as a part in the class that implements the custom control:
[TemplatePart(Name = MapZoomSliderName, Type = typeof(Slider))]
In the OnApplyTemplate() override, I get the Slider:
MapZoomSlider = (Slider) GetTemplateChild("MapZoomSlider");
if (null != MapZoomSlider)
{
MapZoomSlider.ValueChanged +=new RoutedPropertyChangedEventHandler<double>(MapZoomSlider_ValueChanged);
MapZoomSlider.Value = InitSliderValue; // crash
_lastSliderValue = MapZoomSlider.Value;
}
When I try to set the Slider's Value property, the app crashes with "Object reference not set to an instance of an object."
Getting the slider's value works as expected.
What do I need to do to set the Slider's value at run time?
Thanks for any tips...

What is "InitSliderValue"? Maybe its the wrong value type? (Must be a double) Also, zero or negative may not be a valid value.

It appears the problem was in setting the ValueChanged handler before changing the Value property. The ValueChanged handler tries to manipulate other parts of app, parts that might not be ready yet.
If I set the value, then add the handler, it works as desired.
MapZoomSlider.Value = InitSliderValue; // all good
MapZoomSlider.ValueChanged +=new RoutedPropertyChangedEventHandler<double>(MapZoomSlider_ValueChanged);

Related

Failed to set () user defined inspected property on (UIButton)

In a simple ViewController, I added one UIButton.
I would like to use "User Defined Runtime Attributes". For now I added the default Bool attribute.
The button is an #IBOutlet:
#IBOutlet var button:UIButton!
The link in the storyboard is done.
I have nothing else in my app.
I got this error:
2017-03-26 20:31:44.935319+0200 ISAMG[357:47616] Failed to set (keyPath) user defined inspected property on (UIButton):
[<UIButton 0x15e3a780> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key keyPath.
I don't understand what I'm missing.
EDIT 1
Following the advices #timaktimak, I created a custom UIButton class:
#IBDesignable
class ChoiceButton: UIButton {
#IBInspectable var keyPath: Bool! {
didSet {
print("didSet viewLabel, viewLabel = \(self.keyPath)")
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
}
The error is the same:
2017-03-26 22:32:27.389126+0200 ISAMG[429:71565] Failed to set (keyPath) user defined inspected property on (ISAMG.ChoiceButton):
[<ISAMG.ChoiceButton 0x14e8f040> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key keyPath.
Well, your button doesn't have a property called keyPath to be setting it to something.
User Defined Runtime Attributes are used to simply set a property value in the Interface Builder.
You can use a standard property that every UIButton has, for example backgroundColor:
Or, you can create a custom UIButton subclass, add a property to it, then set the button's class to the created custom subclass and set the property value in the User Defined Runtime Attributes section.
You can check out, for example, this answer, it contains an example of a custom class with User Defined Runtime Attributes: https://stackoverflow.com/a/24433125/3445458
Edit: make sure you are not using optional (or implicitly unwrapped optional) for the type, it doesn't work with User Defined Runtime Attributes (I guess because the Interface Builder doesn't know whether the value can be set to nil, and so show it in the options or not).
So you can't do
var keyPath: Bool!
instead you can only do
var keyPath: Bool = false
or don't use a default value and set it in the constuctor instead. For some reason it works with an optional String (and in the example they use an optional String), but it doesn't with optional Bool. To conclude, don't use or worry about User Defined Runtime Attributes too much, it is clearer to set the default values in code!
Hope this helps! Good luck!
Since this thread came up about 5 times in my search for a solution, I wanted to post my fix here for future frustrated souls. The reason I kept getting this error is because Interface Builder doesn't clean up old IBInspectable properties if you're experimenting with them.
In my case, I started with an Int property called type and set a few custom buttons on my screen to use a value of 3 as a test. Later, I changed it to a more meaningful property name and made it a string, then set a few of those. But when I ran the app, I was getting this message.
I didn't discover the error until I went to the Identity Inspector and noticed that I still had an old keypath and value in use for some of the buttons:
Removing the old keypath fixed the problem.
In User Defined Runtime Attributes use layer.cornerRadius instead of cornerRadius.
Looks like failed to set keyPath bool value to true of false.
setKeyPath to true or false!
Compare #IBInspectable(in sub class) and Key Path(identity inspector). Remove key value which is not in #IBInspectable.
Another way to set the set a property value in the Interface Builder is that create an IBOutlet of your view on your view controller.
#IBOutlet weak var yourView: UIView!
yourView.layer.shadowOffset = CGSize(width: 0, height: 1)
yourView.layer.shadowColor = UIColor.lightGray.cgColor
yourView.layer.shadowOpacity = 1
yourView.layer.shadowRadius = 5
yourView.layer.masksToBounds = false
yourView.layer.cornerRadius = 20

CALayer: Where does the implicit animation comes from?

I'm new to Core Animation. While learning about implicit animations, I came across a question.
I put a layer on the screen and made it color change to a random value when pressing a button, which triggers an implicit animation.
#define RANDOM_0_1 (arc4random() / (CGFloat)UINT_MAX)
- (IBAction)changeColor:(id)sender {
CGFloat r = RANDOM_0_1;
CGFloat g = RANDOM_0_1;
CGFloat b = RANDOM_0_1;
self.colorLayer.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0f].CGColor;
}
It works pretty well. Then I use
NSLog(#"%#", [self.colorLayer actionForKey:#"backgroundColor"]);
to get the animation object passed implicitly to the layer. I got
<CABasicAnimation: 0x7f8ae1d21970>.
Referring to the document, I learned that there are four way for a layer to get an action. 'Delegate, actions dictionary, style dictionary and +[CALayer defaultActionForKey:]' Then I stated wonder which step does the animation object really comes from. So I wrote this to check
NSLog(#"%# %# %#", self.colorLayer.delegate, self.colorLayer.actions, self.colorLayer.style);
It gave me three (null)
(null) (null) (null)
As the document said, these values should be set to nil by default.
So it must be +[CALayer defaultActionForKey:] that gives me the animation object. However, when I call
NSLog(#"%#", [self.colorLayer actionForKey:#"backgroundColor"]);
it still gave me a (null).
That's quite strange I thought. I started wonder if the 'key' passed into has been somehow changed by the internal implementation. So I referred to this post to print the argument passed to the method as well as the return value.
static id<CAAction> (*__originalCALayerDefaultActionForKey)( CALayer *, SEL, NSString *) ;
static id<CAAction> CALayerDefaultActionForKey( CALayer * self, SEL _cmd, NSString * event )
{
id res = (*__originalCALayerDefaultActionForKey)( self, _cmd, event );
NSLog(#"%#<%p> %# %#\n", [ self class ], self, event, res ) ;
return res;
}
I got the result like this
CALayer<0x106da5ef0> position (null)
CALayer<0x106da5ef0> bounds (null)
CALayer<0x106da5ef0> backgroundColor (null)
the 'key' passed into was exactly the property name, but it always returns null.
So, could anyone explain where does the animation object comes on earth, or provide me some technique to find out the answer?
Thanks. :)
The CALayer's SDK default animations are implemented in the .m, which you cannot get it in normal way.
And the SDK offers optional ways for user to use customised animation:
delegate:
You can use a delegate object to provide the layer’s contents, handle the layout of any sublayers, and provide custom actions in response to layer-related changes. The object you assign to this property should implement one or more of the methods of the CALayerDelegate informal protocol. ...
actions:
The default value of this property is nil. You can use this dictionary to store custom actions for your layer. The contents of this dictionary searched as part of the standard implementation of the actionForKey: method.
they're just for animation customisation.
e.g. you can create a custom CALayer subclass (say CustomLayer) which override the -actionForKey:
- (id<CAAction>)actionForKey:(NSString *)event
{
if ([event isEqualToString:#"strokeStart"] || [event isEqualToString:#"strokeEnd"]) {
CABasicAnimation * strokAnimation = [CABasicAnimation animationWithKeyPath:event];
strokAnimation.removedOnCompletion = NO;
strokAnimation.fillMode = kCAFillModeForwards;
strokAnimation.duration = .3f;
strokAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
strokAnimation.fromValue = [self.presentationLayer valueForKey:event];
return strokAnimation;
}
return [super actionForKey:event];
}
in this way, whenever you update strokStart & strokend for your CustomLayer instance, it'll invoke the strokAnimation you offered instead of the default animation that SDK implemented.
But style, it's for setting the animatable properties' value w/o animation (cause by default, whenever you update CALayer instance's animatable properties' value, it'll invoke default animation if you don't offer custom one):
An optional dictionary used to store property values that aren't explicitly defined by the layer.
If the style dictionary does not define a value for an attribute, the receiver’s defaultValueForKey: method is called. The default value of this property is nil.
e.g. like strokeStart & strokeEnd, their default values are 0.f & 1.f, you can modify style to use customized default value, like:
aLayer.style = #{#"strokeStart" : #.2f,
#"strokeEnd" : #.6f};
in this way, the layer's animation won't be invoked, like in the initialisation step, especially when you've offered a complex animation for the key, it'll helps a lot.
For +defaultActionForKey:, it's more like user defined default actions, as the doc said:
Returns a suitable action object for the given key or nil of no action object was associated with that key.
Classes that want to provide default actions can override this method and use it to return those actions.
You can override this method like -actionForKey:, below is the order to check whether the action exists when you modified the related property:
Check whether the delegate exists & delegate method -actionForLayer:forKey: is implemented;
Check whether the action of key was offered in -actionForKey:;
Check whether the action of key exists in actions while invoking [super actionForKey:event] after step 2;
Follows step 3 if no action of key exists in actions, check whether the action of key exists in +defaultActionForKey:.
Use SDK implemented default actions, which we cannot get (or might have unknown event name), as far as I know.
Generally, +defaultActionForKey: is like a global customised setting in class level for your CALayer subclass, while actions, -actionForLayer:forKey: & -actionForKey: to handle special layers.

Weird error in accessing the text of UIButton in swift

When I write a simple function such as this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel.text);
}
It gives me an error: UILabel doesn't have a label called text.
However, when I change it to this:
#IBAction func buttonTapped(theButton:UIButton) {
println(theButton.titleLabel?.text);
}
It works fine, but it prints out something like this:
Optional("1");
What I am doing wrong? I am expecting a value of 1. But it is printing out Optional("1") and secondly, it is working fine when println(theButton.titleLabel?.text);
You can get directly from
let title = theButton.currentTitle!
Optional chaining makes the result optional, so you are printing optional value: https://developer.apple.com/library/ios/documentation/swift/conceptual/Swift_Programming_Language/OptionalChaining.html
With optional binding you can print the value only if it exits.
if let text = theButton.titleLabel?.text {
println(text)
} else {
// text doesn't have value
}
#Kirsteins's answer shows how to obtain the button label text in a safe manner.
Remember that:
UIButton has a titleLabel, which is an optional UILabel.
UILabel has a text property, which is an optional String
so there are 2 optionals in the chain. You can use optional binding as in #Kirsteins's answer, or use forced unwrapping:
let text = theButton.titleLabel!.text!
which however I discourage using, because if any of the 2 is nil you'll have a runtime exception. But for completeness it's worth mentioning.
The buttons titleLabel property returns an optional UILabel, that means it's possible that the button doesn't have a titleLabel.
var titleLabel: UILabel? { get }
If you don't set a title to the button, then the button doesn't have a titleLabel property, the iOS framework adds the titleLabel only if the button has a title, I think this happens to reduce memory.
This is why you have to put the "?" (is called optional chaining you can read about it here http://bit.ly/1vrSOi1) in that line, but this usually get auto completed by Xcode itself.
Kirsteins answers it correctly but misses one small detail
if your object can be nil (optional) you need to check first if it exists to then access its value, like this:
if let text = theButton.titleLabel?.text {
println(text)
}
but you can also ignore the if and just call it like this:
let text : String = theButton.titleLabel?.text
// If theButton.titleLabel don't exists then text will be nil
this happen if the IBOutlet was declared with ? but if you declare with ! that means you know that it could be nil, but you never want it to be nil, for a IBOutlet i prefer this approach since if the IBOutlet is not connected then maybe something is worn with my code.
#IBOutlet var theButton : UIButton!
// and get text value as
theButton.titleLabel!.text
this will ensure theButton.titleLabel could be nil, but in this part of code it is required, hope this helps to understand the difference between optional (?) and optional required (!)

Xcode6 autogetters and autosetters

Xcode6 ios swift
I have created my own class and trying to make an autogetter and autosetter, but i don't really know if it's allowed.
var Birthday:NSDate {
get {return birthday}
set(newValue){birthday = newValue}
}
var BirthYear:Int32 {
get {}
set {}
}
The last part of code triggers error, missing return, so my question is that - Is there any possibility to make getter and setter without making a second variable
Stored properties in swift are backed by hidden instance variables - the property itself is its own getter and setter, unless you implement it as a computed property, in that case you have to provide your own getter and/or setter. So when you write:
var birthday: NSDate
you use it as:
let value = classInstance.birthday
to read its value, and
classInstance.birthday = someDate
to assign a new value. You don't have to do anything special to make that work.
Suggested reading: Properties
Side note: by convention variables and property should use lower camel case notation, so they should start with lowercase, and if made up of multiple words, make the first letter of each word in uppercase. For instance:
var single: Int
var multipleWordsVariable: String

How does sender.selected = ! sender.selected toggle between selected states?

I'm very new to Objective-C so sorry if this is extremely obvious to many of you, but I'm trying to work out how the following piece of code is actually working:
- (IBAction)chooseColour:(UIButton *)sender {
sender.selected = !sender.isSelected;
}
Now it obviously toggles between the selected and unselected states of the button sending the action, but what is the code 'sender.selected = !sender.isSelected' actually saying? Is it just 'set the sender selected property to the opposite (i.e. ! not) of the getter'? So if the getter is 'getting' the current selected value as true then it sets the selected property as !true i.e false. Or is this a piece of convenience code that I'm not yet privy to? Because it also seems that '!sender.isSelected' simply means not selected as in
if (!sender.isSelected){
statement
}
i.e. do statement if the sender is not selected. This is no doubt really obvious, just I'm a bit confused with it at the moment.
Thanks!
You are entirely correct, it's calling the getter to obtain the value and calling the setter with the NOT (!) of the value. It isn't Objective-C, it's plain C syntax.
Is it just 'set the sender selected property to the opposite (i.e. ! not) of the getter'?
Exactly. That.
Or is this a piece of convenience code that I'm not yet privy to?
No, the only piece of syntactic sugar is the dot notation for getters/setters, but you are already aware of it.
The portion of the code:
sender.selected = !sender.isSelected;
Basically inverts the selection. It asks the question Is this false? so true evaluates false, and false evaluates to true. So it's a toggle.
from documentation :
#property(nonatomic,getter=isSelected) BOOL selected; // default is NO may be used by some subclasses or by application
//explanation
if you use ![sender isSelected] value in property isn't changed. then if you use setter sender.selected = ![sender isSelected] - new value is set to the sender (selected property). then run getter sender isSelected return new value, uff i hope it helps

Resources