Swift (from the book 《iOS Animations by Tutorials:Chapter 12》 released by http://www.raywenderlich.com/):
let photoLayer = CALayer()
#IBInspectable
var image: UIImage! {
didSet {
photoLayer.contents = image.CGImage
}
}
How can I implement the above syntax in objective-c?
I know only to set the property of photoLayer and image like below:
#property (strong, nonatomic) CALayer *photoLayer;
#property (strong, nonatomic) IBInspectable UIImage *image;
But i do not know how to implement didset{...} parts using objective-c syntax, please help!
override the setter and implement the setter yourself.
- (void)setImage:(UIImage *)image {
if (_image != image) {
_image = image;
photoLayer.contents = image.CGImage;
}
}
Swift needs special language features for this, because of its static nature, one cannot subclass at runtime.
In Objective-C this is not necessary, so you have no language feature for this. You can use key-value observation.
But if the observing instance is the instance whose property changed, you can do it with subclassing as mentioned by #Daij-Djan. (Basically this is what you do in Swift.) Additionally in Objective-C you have the ability to observe the properties of a different instance of a different class.
Related
I am converting some Swift code to Objective C. not much familiar with Swift syntax.
override var layer: CAShapeLayer {
get {
return super.layer as! CAShapeLayer
}
}
I don't understand how to override this property in objective C.
I tried this
in .h file
#property (nonatomic, readonly, retain) CAShapeLayer *layer;
in .m file
#synthesize layer;
but it seems to not work. There is no error but the code is not executing as expected.
What you need to do now is just implement the getter for the layer property:
-(CAShapeLayer *)layer
{
return (CAShapeLayer *)super.layer
}
I need to connect an IBOutlet from this UIImageView to my ViewController in order to perform animations on it (just swiping a stone-like block around a grid).
However, I already have a category file that has an (+instancetype) method to create the UIImage programmatically, so I can use it to set an instance variable (usable throughout other files). Problem is (and bear with me because I am new to performing animation methods on movable objects) I can't use that instance variable (which is a UIView*) to do any animations. Actually, let me know if there is a way (to be used with the +(void)animateWithDuration.... method) because it would be beneficial.
+ (instancetype)stoneOneCreator {
UIImage* stoneOneImage = [UIImage imageNamed:#"Stone.png"];
UIImageView* stoneOneView = [[UIImageView alloc] initWithImage:
stoneOneImage];
return stoneOneView;
}
I call this method in my ViewController, set it to an IV, then use that IV throughout the other files where needed.
#implementation
{
UIView* _one;
}
_one = [UIView stoneOneCreator];
Then use _one by passing into other methods that are implemented in other files etc. etc.
My outlet property though when I'm working with the storyboard is
#property (weak, nonatomic) IBOutlet UIImageView* stoneOne;
Is there a way to use the 'stoneOne' property for IV purposes similar to '_one'? Or, is there a way to tie together the '_one' IV with the 'stoneOne' property?
Thanks,
Anthony
This is a trick I use a lot in android:
I take an image with Image Picker: camera or gallery
I put the image in an ImageView
I also put the image url in the ImageView, in the tag field
so later when I need the url of an image (to send to server), I grab it from the imageView's tag.
But tag in iOS is a bit different from android: in iOS it's just a number. So is there such a way of piggybacking on an UIButton on iOS: basically any field whatsoever that is available for storing a text and which the user cannot see?
I'm not aware of an analogous field on a UIImageView. Your best bet may be to subclass UIImageView to add such a property. In the .h file for the new class, do something like:
#interface SubclassedUIImageView : UIImageView
#property (strong, nonatomic, copy) NSString *url;
#end
Then assign the url value to SubclassedUIImageView in imagePickerController: didFinishPickingMediaWithInfo:. Assuming you're using Interface Builder, to use the subclassed UIImageView, you drop a UIImageView control onto the parent view, go to the Identity inspector, and change the Custom Class field to the name of your subclassed UIImageView.
Natively, there's no way to do this. However, you can use a category and store the text in an associated object:
#interface UIImageView (StringTagAdditions)
#property (nonatomic, copy) NSString *stringTag;
#end
#implementation UIImageView (StringTagAdditions)
static NSString *kStringTagKey = #"StringTagKey";
- (NSString *)stringTag {
return objc_getAssociatedObject(self, kStringTagKey);
}
- (void)setStringTag:(NSString *)stringTag {
objc_setAssociatedObject(self, kStringTagKey, stringTag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
#end
More info # http://nshipster.com/associated-objects/
This type of thing has been asked before, but you could pull this off with a UIButton category that adds getter/setter like methods that store this value for you:
setting new properties in category interface/implementation
Objective-C: Property / instance variable in category
http://iosdevelopertips.com/objective-c/adding-properties-category-using-associated-objects.html
Or you could subclass UIButton and add the property you need. There are lots of options, but I'm unclear on what you mean by "the user cannot see"?
I'm new to objective-c and found some OO features I have learned from other language is different in objective-c. The most confusing question for me until now is how to reimplement a property in subclass, or should I do this?
For example, I have two view controllers GameViewController and CardGameViewController.
#interface GameViewController : UIViewController {
Game *_game;
}
#property (nonatomic) Game *game;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (weak, nonatomic) IBOutlet UILabel *scoreLabel;
#property (weak, nonatomic) IBOutlet UILabel *messageLabel;
#end
#interface CardGameViewController : GameViewController
#property (nonatomic) CardMatchingGame *game;
#end
CardMatchingGame is derived from Game. #property game in GameViewController is implemented like below:
- (Game *)game
{
if (!_game) {
_game = [[Game alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]];
}
return _game;
}
I tried to reimplement #property game like below but got a warning in the return clause which said Incompatible pointer types returning 'Game *' from a function with result type 'CardMatchingGame *'.
- (CardMatchingGame *)game
{
if (!_game) {
_game = [[CardMatchingGame alloc] initWithCardCount:[self.cardButtons count]
usingDeck:[self createDeck]
matchThreshold:self.threshold.selectedSegmentIndex + 2];
}
return _game;
}
Is this the correct way of reimplementing a property in subclass? If not, what am I supposed to do?
You can't reimplement like that (well, you could do a ton of casting, but that isn't a good option).
One option is to create the property as #property (nonatomic) id game; and then it can hold any type of Game class (or subclass) that you have. And, because it is id type the compiler will assume that any method you try to call is valid. Obviously you could make mistakes which you won't find till later.
Another, better, option is to add a new property in the subclass as #property (nonatomic) CardMatchingGame *cardGame; and, in the subclass, when a new game is created the CardMatchingGame instance is stored in both game and cardGame properties. Now, the superclass has access to game and can use it, and the subclass has access to cardGame, which is typed as the correct class. So, you have compiler checks and usage of the same instance in both classes. But, you haven't reimplemented a property - you have multiple properties, each doing the appropriate task for their location + purpose.
If CardMatchingGame is a subclass of Game you can override - (Game *)game and return your CardMatchingGame object. Just make sure not to change the return type.
I think the problem is ivar in parent class Game *_game; it produce warning you see. Try to use different name or id type for it.
or just cast it before return (CardMatchingGame *)_game;
I have a project using CoreData. I use Mogenerator to generate the subclasses.
When I set the value of a property, this value isn't actually assigned. Each subsequent time I try to set the value, the previous value I set it to was not assigned.
This worked fine as my underlying data framework was Mantle, but since moving to CoreData, this stopped working. I rely on KVO to keep some UIView objects up-to-date with the model.
Again, the ivars of a CoreData NSManagedObject subclass do not seem to take the values I assign them.
Consider the following interface:
#interface Light : _Light{}
/**
Light / Color Properties
*/
#property (nonatomic, assign) CGFloat brightness; // 0...1
#property (nonatomic, assign) CGFloat hue; // 0...1
#property (nonatomic, assign) CGFloat saturation; // 0...1
#property (nonatomic, assign, getter = isEnabled) BOOL enabled;
#property (nonatomic, readonly) UIColor *color; // derived from the above
- (void)setHue:(CGFloat)hue saturation:(CGFloat)saturation; // it often makes sense to set these together to generate fewer KVO on the color property.
#end
and the following .m file:
#interface Light ()
{
CGFloat _hue, _saturation, _brightness;
UIColor *_color;
}
#property (nonatomic, assign) BOOL suppressColorKVO;
#property (nonatomic, readwrite) UIColor *color;
#end
#implementation Light
#synthesize suppressColorKVO = _suppressColorKVO;
- (void)setHue:(CGFloat)hue saturation:(CGFloat)saturation
{
BOOL dirty = NO;
if (saturation != _saturation) {
// clamp its value
[self willChangeValueForKey:#"saturation"];
_saturation = MIN(MAX(saturation, 0.0f), 1.0f);
[self didChangeValueForKey:#"saturation"];
dirty = YES;
}
if (hue != _hue) {
[self willChangeValueForKey:#"hue"];
_hue = MIN(MAX(hue, 0.0f), 1.0f);
[self didChangeValueForKey:#"hue"];
dirty = YES;
}
if (dirty) {
if (!_suppressColorKVO) {
[self setColor: self.color];
}
}
}
// other stuff... the color accessors are also custom. Derived from the h, s, b values.
#end
I assume I'm not playing nice with CoreData, but I have no idea what's wrong. These hue, saturation, brightness are all 'transient' (not in the core data sense) because they are constantly updated by some hardware we are interfacing with so there's no need to save their state.
If hue and saturation are properties in your model then you should be setting their values using setPrimitiveValue:forKey: (or the associated generated primitive methods).
That said, your code all looks custom as model attributes would be NSNumber instances and mogenerator would create value methods for you. So I'm going to guess that these attributes you have aren't backed in the model and that's why they aren't being stored.
So, add the attributes to the model and access the values using the appropriate methods.
In the end it had nothing to do with CoreData. This approach above DOES work with CoreData objects. You can have in a subclass some "transient" properties that exist outside of the CoreData NSManagedObject and you can create your own ivars for them, and your own accessors, and it cooperates.
In relation to this question, I have a complex system that also sends some commands to some hardware, and the hardware returns a response whether it accepted the command with the current status. It turns out I had a bug in that handler which was setting these values back to some unexpected value.
What helped me debug it were using watchpoints in the debugger. Really handy feature! You set a watchpoint and it will break in the debugger whenever the memory address of your variable is set with a new value. (A tiny bit more on that here)