Sorry for a newbie question but can someone help with translating this to Swift?
-(instancetype)init
{
self = [super initWithImageNamed:#"character.png"];
{.
self.name = playerName;
self.zPosition = 10;
}
return self;
}
it's for a child of SKSpriteNode
When I try to call super.init(imageNamed: "character.png") I get an error saying `Must call a designated Initialiser of the superclass SKSpriteNode.
If I try to just write it like this:
init() {
super.init()
self.name = playerName
self.zPosition = 10
}
I get an error in my GameScene when I call:
var player : Player = Player(childNodeWithName(playerName))
I get an error about converting the type to string.
issue 1
a subclass initializer MUST call the superclass's designated Initializer.
for SKSpritenode that's initWithTexture: color: size:
SO
a SKTexture can be made from an image directly:
let texture = SKTexture(imageName: "character.png")
a color:
let color = UIColor.clearColor()
then:
super.init(texture: texture color:color size:texture.size)
issue 2:
class player needs an initialiser that takes a SKNode:
init(node: SKNode) {
...
}
Related
I had a class UIBaseClassViewController with convenient functions in objective c.Now i'm switching to swift and i'm trying to convert it's code into swift.the function giving me problem is
+(UIBaseClassViewController*)getController
{
return [[[self class] alloc] initWithNibName:NSStringFromClass([self class]) bundle:[NSBundle mainBundle]];
}
i was able to convert it but it's not working fine
static func getController() -> Self
{
print("sam controller class = \(String(describing:self))")
print("SAM controller = \(self.init(nibName: String(describing:self), bundle:Bundle.main))")
return self.init(nibName: String(describing:self), bundle:Bundle.main)
}
Output:
sam controller class = UILoginViewController
SAM controller = <Swift_And_Node.UIBaseClassViewController: 0x7f8a4ee13830>
created object is of type UIBaseClassViewController.it loads the nib fine but as object is of UIBaseClassViewController app crashes because it was not able to find functions in UIBaseClassViewController which are in UILoginViewController.
How can i make it create object of child class instead of parent.UILoginViewController in this case
for better Understanding showing adding code:
UIBaseClassViewController:
class UIBaseClassViewController: UIViewController {
static func getController() -> Self
{
print("sam controller class = \(String(describing:self))")
print("SAM controller = \(self.init(nibName: String(describing:self), bundle:Bundle.main))")
var object = self
return self.init(nibName: String(describing:self), bundle:Bundle.main)
}
}
UILoginViewController:
class UILoginViewController: UIBaseClassViewController {}
3rd controller who need UILoginViewController:
UILoginViewController.getController()
You either have to call this static function on desired view controller class or not making it static at all. Please see the example below to see how it works in Swift.
class ParentView: UIView {
static func printSelf() {
print(String(describing: self))
}
}
class ChildView: ParentView {}
ParentView.printSelf() // Prints ParentView
ChildView.printSelf() // Prints ChildView
Turns out we don't need to mention nib and bundle for controller object...I moved from objective c and these things are necessary there.
with
[[UILoginViewController alloc] init]
app will show black screen.
In swift we can just use UILoginViewController() and it will automatically associate nib with the controller object.
so to answer my question i just used
self.init()
instead of
self.init(nibName: String(describing:self), bundle:Bundle.main)
I have a subclass of SKScene called SPGamePlayScene. It does a bunch of stuff. I need to make another scene that does the same stuff as SPGamePlayScene, but a little more/little differently, so I thought I would subclass my SPGamePlayScene for this new scene, SPPracticeScene. however im having issues with my class level instantiation methods.
below is the method of SPGamePlayScene:
//SPGamePlayScene.m
+ (instancetype)sceneWithSize:(CGSize)size colored:(BOOL)colored {
SPGamePlayScene *gamePlayScene = [SPGamePlayScene sceneWithSize:size];
gamePlayScene.colored = colored;
gamePlayScene.backgroundColor = [SKColor whiteColor];
return gamePlayScene;
}
this works fine. However in my subclass of SKGamePlayScene, SPPracticeScene, I need to create an SPPracticeScene object using its super class's sceneWithSize colored method.
//SPPracticeScene.m which inherits from SPGamePlayScene
+ (instancetype)sceneWithSize:(CGSize)size {
//this keeps giving me a SPGamePlayScene but I need it to be a SPPracticeScene
SPPracticeScene *practiceScene = (SPPracticeScene *)[self sceneWithSize:size colored:NO];
//this line throws an exception because practiceHud is not a property of SPGamePlayScene
practiceScene.practiceHud = [SPPracticeHud practiceHudAtPosition:CGPointMake(0, practiceScene.frame.size.height - 20) inClef:[SPGameState sharedInstance].clef inFrame:practiceScene.frame];
[practiceScene addChild:practiceScene.practiceHud];
return practiceScene;
}
I know that this returns an SPGamePlayScene so I tried casting it with no luck. The line that sets the practiceHud property causes a crash since SPGamePlayScene does not have that property (even though its supposed to be an SPPracticeScene. This is my first foray into custom subclasses/inheritance so im probably misunderstanding something about the way things need to be done/what types need to be returned etc. How can I make this work?
I ended up just overriding the initWithSize method and using an instance level initializer instead of a class level one. using an init method returning an id seemed to do the trick.
//SPGamePlayScene.m
(id)initWithSize:(CGSize)size colored:(BOOL)colored {
if (self = [super initWithSize:size]) {
//some custom code here
}
return self;
}
//SPPracticeScene.m which inherits from SPGamePlayScene
- (id)initWithSize:(CGSize)size {
//init it using the SPGamePlayScene custom initializer
if (self = [super initWithSize:size colored:NO]) {
//some custom code here
}
return self;
}
using the above code let me use the custom initializer for SPPracticeScenes super class, but add additional functionality to it that I needed, while making sure the object returned was of the correct type. I would love to know how I could have made it work with my class level initializers though.
Is it possible to alter the definition of a class in Objective-C?
For example, I have a function that creates objects (bullets), and those bullets are all the same. However, if given an event, say a power-up, I want to alter those bullets. In either size, or color, or what-have-you.
In my code, I have
playerBullet = [[PlayerBullet alloc] init];
But that always initializes the new bullet, regardless of 'power-up', as the standard, template bullet.
Is there a way that I adjust the class definition such that all new allocations of the PlayerBullet class come with the new value?
PlayerBullet.setProjectileColor:#"red";
Or is this approach a bad one?
The answer to your question as asked is No. However, what you are trying to do is very easily achievable with the proper design.
You should create a customer initialization method in your PlayerBullet class that takes an argument.
- (id) initWithProjectileColor:(NSString*)color
{
self = [self init];
if (self)
{
self.projectileColor = color;
}
return self;
}
So you could make a method and then call something like:
[[PlayerBullet alloc] initWithProjectileColor:#"red"];
Alternatively, you could create public properties and set them after creating a "blank" projectile.
call [[PlayerBullet alloc] initWithColor:#"red"] where you need it:
-(id) initWithColor:(NSString*) color{
self = [self init];
[self setProjecticeColor:colour];
return self;
}
There's no way to do this automatically. You could do it yourself with something like this. (Memory management not included.)
static NSString *DefaultProjectileColor = #"black";
+(void) setDefaultProjectileColor:(NSString *)color {
DefaultProjectileColor = color;
}
+(NSString *) defaultProjectileColor {
return DefaultProjectileColor;
}
-(id) init {
...
self.projectileColor = [PlayerBullet defaultProjectileColor];
...
}
-(void) gotPowerUp {
...
[PlayerBullet setDefaultProjectileColor:#"red"];
...
}
I am trying to get an Animation Helper with a cocos2d project and for some reason keep getting an error message:
unrecognized selector sent to class.
I have tried numerous approaches to no avail. I understand that it may have to do with a class-instance conflict, but I do not know how to resolve it. Any thoughts?
This is how I am calling the helper function:
CCAnimation* anim = [CCAnimation animationWithFrame:playerAnimName frameCount:1 delay:0.08f];
And this is the helper function itself:
+(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay
{
printf("start helper");
// load the players's animation frames as textures and create a sprite frame
NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount];
for (int i = 1; i < frameCount+1; i++)
{
NSString* file = [NSString stringWithFormat:#"%#%i.png", frame, i];
CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:#"maze-art.plist"];
CCSpriteFrame* frame = [frameCache spriteFrameByName:file];
[frames addObject:frame];
}
// return an animation object from all the sprite animation frames
return [CCAnimation animationWithSpriteFrames:frames delay:delay];
}
Any insight is appreciated. Thanks!
In order for category to work, you must define the new method in the following way. Please refer to this http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
#interface ClassName ( CategoryName )
// method declarations
#end
For example, your new method must be defined as
#interface CCAnimation (Helper)
+(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay
{
...
}
#end
Where do your helper method is? If it is inside your own class, that you have to call it as
[MyClass method];
not
[CCAnimation method];
+ means that method is static so you must call it with the class inside which this method is.
I'd like to override UILabel's setText method but I'm not sure that:
A) it's possible, and
B) if maybe there's a better way to achieve what I'm trying to accomplish.
I have a subclass of UIView that has a UILabel property as one of its sub-views. I'd like to know when the UILabel's "text" property changes so I can adjust the size of the rounded rect behind it. If I owned UILabel I'd just override setText...but UILabel is Apple's and its implementation is hidden.
So, how should I be going about this?
Subclasses of UILabel can override the setText method quite easily. I'm not really sure why this hasn't yet been included as a legitimate answer on this 4 year old question.
- (void) setText:(NSString *)text
{
[super setText:text];
[self sizeToFit];
}
You can use Key-Value Observing to track changes to the UILabel.text property. The approach involves three steps:
1) Registering to observe the property, when you load the view
[label addObserver:inspector
forKeyPath:#"text"
options:(NSKeyValueObservingOptionNew |
NSKeyValueObservingOptionOld)
context:NULL];
2) Receiving a notification about any changes:
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqual:#"text"]) {
// put your logic here
}
// be sure to call the super implementation
// if the superclass implements it
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
3) De-registering the observation whenever you aren't interested any more:
[label removeObserver:inspector forKeyPath:#"text"];
Matt answer is good for Objective-C, but doesn't work in Swift (normal, it didn't existed when he answered), the accepted answer from notnoop does work in swift, even though it is more complicated, just to give another idea in swift you can use the didSet:
class MyLabel: UILabel {
override var text: String? {
didSet {
if let text = text {
println("the new text is: \(text)")
} else {
println("the text has been set to nil")
}
}
}
Based on Drix answer, I think this is a more correct approach (using set instead of didSet):
class UnreadCountLabel: UILabel {
override var text: String? {
set {
if let newValue = newValue where !newValue.isEmpty {
super.text = " \(newValue) "
self.hidden = false
} else {
super.text = newValue
self.hidden = true
}
}
get {
return super.text
}
}
}
Are you just using a rounded rectangle as the background for the Label? If that is the case, you can look into using UIIMage stretchableImageWithLeftCapWidth:topCapHeight. This will take an image you've created that has a left and top repeating section with a width you specify and automatically stretch it to your width.
If not, Key-Value observing is the way to go. Just to cover another option--this is like "playing with fire," as Apple programmer Evan Doll said in one of his Stanford lectures--you can use method swizzling to exchange one method implementation for another.
void method_exchangeImplementations(Method m1, Method m2);
In this case, you want to tweak the implementation of setText, but you also want to call the original setText in UILabel. So you could exchange setText with setTextAndUpdateSize, and inside setTextAndUpdateSize do what setText does originally plus add on a little more. If you are confused or think this is a bad idea, it probably is. You can get a Method object to pass into method_exchangeImplementations by calling class_getInstanceMethod([NSSTring class], #selector (methodName).
Once your method swizzle has been called once, inside your new method you can then call the old implementation of setText from within the new one by using, yes, setTextAndUpdateSize. It's confusing and not recommended, but it works. A good example can be found in the developer sample code.
I pulled of Method Swizzling in Swift 2.0. Changing the font of the entire application by swapping the implementation of setText method of the label.
Copy the code in app delegate and use the customSetText to make application level changes
// MARK: - Method Swizzling
extension UILabel {
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
// make sure this isn't a subclass
if self !== UILabel.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("setText:")
let swizzledSelector = Selector("customSetText:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Custom set text method for UI Label
func customSetText(text: String) {
self.customSetText(text)
//set custom font to all the labels maintaining the size UILabel
self.font = UIFont(name: "Lato-LightItalic", size: self.font.pointSize)
}
}