Ways to compare two CCSprite's runAction - objective-c

I have a CCSprite "_wo1" and it has a BOOL property "attack". I have the following code in my update method and I want it to constantly check if "attack" is YES or NO and make my sprite do different kinds of runAction.
So my question is:
How to compare two runAction? I tried "==" and "isEqual" and they are not working..
MySprite *_wo1 = ... // initialize _wo1 using my own class that sub-classed CCSprite
if (![_wo1 attack])
{
_wo1.position = ccp(_wo1.position.x + 10 * dt, _wo1.position.y); // walking forward
if (currentAction == attAction) // currentAction is defined in setting its runAction in the beginning: "currentAction = [wo1 runAction:[s01WalkAction copy]];"
{
[_wo1 stopAction:attAction];
[_wo1 runAction:[walkAction copy]];
}
}
else{
if (currentAction == walkAction)
{
[_wo1 stopAction:walkAction];
[_wo1 runAction:[attAction copy]];
}
}
Any helps or suggestions will be highly appreciated. Thank you!

You can keep an action state of your sprite in the MySprite class which easily can tell you which action is used in the sprite.
When you run an action on the sprite just set the state properly. For example :
mySprite.actionState = ACTION_WALKING;
and then you can easily check which action is used:
if (mySprite.actionState == ACTION_WALKING)
{
// Your handling code
}

Related

Unable to set value via protocol

In my Objective C code I had this:
if ([view conformsToProtocol:#protocol(UITextInputTraits)]) {
id<UITextInputTraits> field = view;
field.enablesReturnKeyAutomatically = YES;
}
Now I'm trying to convert that to swift, so I did this:
if var field = view as? UITextInputTraits {
field.enabledReturnKeyAutomatically = true
}
I'm getting a compiler error saying that 'field' is immutable. What's the right way to accomplish this?
The problem is caused by Swift's peculiar way of dealing with optional protocol requirements. Optional protocol properties have no setter. (I regard this as a bug in the language.) You'll have to work around it.
You can say (horrible):
switch view {
case let field as UITextField:
field.enablesReturnKeyAutomatically = true
case let field as UITextView:
field.enablesReturnKeyAutomatically = true
default: break
}
Another way (equally horrible):
let setter = #selector(setter:UITextInputTraits.enablesReturnKeyAutomatically)
if view.responds(to:setter) {
view.perform(setter, with: 1 as NSNumber)
}

Completion handler for property in swift

I'm trying to use a control called MZFormSheetController in swift. In the example given it provides a property as a completion handler, if I understand correctly, but I'm having difficulties translating it in Swift. Any help would be appreciated.
This is in the obj-c example.
controller.didPresentContentViewControllerHandler = ^(UIViewController *content) {
NSLog(#"DID PRESENT");
[self setNeedsStatusBarAppearanceUpdate];
};
I tried many variations and did an extensive search in the web but I could not find anything that could help me so I'm stuck here
controller.didPresentContentViewControllerHandler = (content:UIViewController() -> () {
println("did present1")
})
Here are the relevant docs: Cocoa Docs:: MZFormSheetPresentationController:: didPresentContentViewControllerHandler
If you need to access the view controller then do it like this,
controller.didPresentContentViewControllerHandler = {
controller in
println("did present1")
}
Or if you dont need the reference to the view controller, you can simply do,
let controller = Controller()
controller.didPresentContentViewControllerHandler = {
_ in
println("did present1")
}
Try add a variable after opening brace
controller.didPresentContentViewControllerHandler = {
vc in
println("did present1")
})

How find out if I'm an NSButton with buttonType NSSwitchButton?

I'm subclassing NSButtonCell to customize the drawing (customizable theme). I'd like to customize the way checkboxes and radio buttons are drawn.
Does anyone know how to detect whether a button is a checkbox or radio button?
There is only -setButtonType:, no getter, and neither -showsStateBy nor -highlightsBy seem to give any unique return values for checkboxes that don't also apply to regular push buttons with images and alternate images.
So far I've found two (not very pretty) workarounds, but they're the kind of thing that'd probably get the app rejected from MAS:
Use [self valueForKey: #"buttonType"]. This works, but since the method is not in the headers, I presume this is something Apple wouldn't want me to do.
Override -setButtonType: and -initWithCoder: to keep track of the button type when it is set manually or from the XIB. Trouble here is the XIB case, because the keys used to save the button type to disk are undocumented. So again, I'd be using private API.
I'd really like this to be a straight drop-in replacement for NSButtonCell instead of forcing client code to use a separate ULIThemeSwitchButtonCell class for checkboxes and a third one for radio buttons.
A button does not know anything about its style.
From the documentation on NSButton
Note that there is no -buttonType method. The set method sets various button properties that together establish the behavior of the type. -
You could use tag: and setTag: (inherited by NSButton from NSControl) in order to mark the button either as a checkbox or a radio button. If you do that programatically then you should define the constant you use. You can also set the tag in Interface Builder, but only as an integer value (magic number).
In initWithCoder, here is my adaptation of the BGHUDButtonCell.m solution, updated for Mac OS Sierra:
-(id)initWithCoder:(NSCoder *)aDecoder {
if ( !(self = [super initWithCoder: aDecoder]) ) return nil;
NSImage *normalImage = [aDecoder decodeObjectForKey:#"NSNormalImage"];
if ( [normalImage isKindOfClass:[NSImage class]] )
{
DLog( #"buttonname %#", [normalImage name] );
if ( [[normalImage name] isEqualToString:#"NSSwitch"] )
bgButtonType = kBGButtonTypeSwitch;
else if ( [[normalImage name] isEqualToString:#"NSRadioButton"] )
bgButtonType = kBGButtonTypeRadio;
}
else
{
// Mac OS Sierra update (description has word "checkbox")
NSImage *img = [self image];
if ( img && [[img description] rangeOfString:#"checkbox"].length )
{
bgButtonType = kBGButtonTypeSwitch;
}
}
}
This is strange to me that it's missing from NSButton. I don't get it. That said, it's easy enough to extend NSButton to store the last set value:
import Cocoa
public class TypedButton: NSButton {
private var _buttonType: NSButton.ButtonType = .momentaryLight
public var buttonType: NSButton.ButtonType {
return _buttonType
}
override public func setButtonType(_ type: NSButton.ButtonType) {
super.setButtonType(type)
_buttonType = type
}
}
Swift 5.5
This is my approach. I use a standard naming convention in my app that relies on plain language identifiers. All my UI elements incorporate their respective property names and what type of UI element is associated with the property. It can make for some pretty long IBOutlet and IBAction names, but remembering tag numbers is way too complicated for me.
For example:
#IBOutlet weak var serveBeerCheckbox: NSButton!
#IBOutlet weak var headSize0RadioButton: NSButton!
#IBOutlet weak var headSize1RadioButton: NSButton!
#IBOutlet weak var headSize2RadioButton: NSButton!
\\ etc.
If there are UI properties that need to be stored, I name those without the type of UI element:
var serveBeer: Bool = true
var headSize: Int = 1
Bare bones example:
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var serveBeerCheckbox: NSButton!
#IBOutlet weak var headSize0RadioButton: NSButton!
#IBOutlet weak var headSize1RadioButton: NSButton!
#IBOutlet weak var headSize2RadioButton: NSButton!
var serveBeer: Bool = true
var headSize: Int = 1
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func buttonClicked(button: NSButton) {
guard let identifier = button.identifier else { return }
if identifier.rawValue.contains("Checkbox") {
switch button.identifier {
case serveBeerCheckbox.identifier:
// Do something with the Checkbox
serveBeer = (serveBeerCheckbox?.state == .on)
default:
// Another checkbox button
}
} else if identifier.rawValue.contains("RadioButton") {
switch button.identifier {
case headSize0RadioButton.identifier:
headSize = 0
case headSize1RadioButton.identifier:
headSize = 1
case headSize2RadioButton.identifier:
headSize = 2
default:
}
} // You could continue checking for different types of buttons
print("Serve beer? \(serveBeer ? "Sure!" : "Sorry, no.")")
if serveBeer {
switch headSize {
case 1:
print("With one inch of head.")
case 2:
print("With two inches of head!")
default:
print("Sorry, no head with your beer.")
}
}
}
}
As you can see, one could write a very generic method that can work on any type of UI element and use the rawValue of the identifier string with .contains() to isolate the type of element being worked with.
I have found using this approach allows me to initialize a UI with a lot of different elements pretty quickly and efficiently without having to recall tag numbers.

iOS: How to convert UIViewAnimationCurve to UIViewAnimationOptions?

The UIKeyboardAnimationCurveUserInfoKey has a UIViewAnimationCurve value. How do I convert it to the corresponding UIViewAnimationOptions value for use with the options argument of +[UIView animateWithDuration:delay:options:animations:completion:]?
// UIView.h
typedef enum {
UIViewAnimationCurveEaseInOut, // slow at beginning and end
UIViewAnimationCurveEaseIn, // slow at beginning
UIViewAnimationCurveEaseOut, // slow at end
UIViewAnimationCurveLinear
} UIViewAnimationCurve;
// ...
enum {
// ...
UIViewAnimationOptionCurveEaseInOut = 0 << 16, // default
UIViewAnimationOptionCurveEaseIn = 1 << 16,
UIViewAnimationOptionCurveEaseOut = 2 << 16,
UIViewAnimationOptionCurveLinear = 3 << 16,
// ...
};
typedef NSUInteger UIViewAnimationOptions;
Obviously, I could create a simple category method with a switch statement, like so:
// UIView+AnimationOptionsWithCurve.h
#interface UIView (AnimationOptionsWithCurve)
#end
// UIView+AnimationOptionsWithCurve.m
#implementation UIView (AnimationOptionsWithCurve)
+ (UIViewAnimationOptions)animationOptionsWithCurve:(UIViewAnimationCurve)curve {
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
}
}
#end
But, is there an even easier/better way?
The category method you suggest is the “right” way to do it—you don’t necessarily have a guarantee of those constants keeping their value. From looking at how they’re defined, though, it seems you could just do
animationOption = animationCurve << 16;
...possibly with a cast to NSUInteger and then to UIViewAnimationOptions, if the compiler feels like complaining about that.
Arguably you can take your first solution and make it an inline function to save yourself the stack push. It's such a tight conditional (constant-bound, etc) that it should compile into a pretty tiny piece of assembly.
Edit:
Per #matt, here you go (Objective-C):
static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
switch (curve) {
case UIViewAnimationCurveEaseInOut:
return UIViewAnimationOptionCurveEaseInOut;
case UIViewAnimationCurveEaseIn:
return UIViewAnimationOptionCurveEaseIn;
case UIViewAnimationCurveEaseOut:
return UIViewAnimationOptionCurveEaseOut;
case UIViewAnimationCurveLinear:
return UIViewAnimationOptionCurveLinear;
}
}
Swift 3:
extension UIViewAnimationOptions {
init(curve: UIViewAnimationCurve) {
switch curve {
case .easeIn:
self = .curveEaseIn
case .easeOut:
self = .curveEaseOut
case .easeInOut:
self = .curveEaseInOut
case .linear:
self = .curveLinear
}
}
}
In Swift you can do
extension UIViewAnimationCurve {
func toOptions() -> UIViewAnimationOptions {
return UIViewAnimationOptions(rawValue: UInt(rawValue << 16))
}
}
An issue with the switch based solution is that it assumes no combination of options will be ever passed in. Practice shows though, that there may be situations where the assumption doesn't hold. One instance I found is (at least on iOS 7) when you obtain the keyboard animations to animate your content along with the appearance/disappearance of the keyboard.
If you listen to the keyboardWillShow: or keyboardWillHide: notifications, and then get the curve the keyboard announces it will use, e.g:
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
you're likely to obtain the value 7. If you pass that into the switch function/method, you won't get a correct translation of that value, resulting in incorrect animation behaviour.
Noah Witherspoon's answer will return the correct value. Combining the two solutions, you might write something like:
static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve)
{
UIViewAnimationOptions opt = (UIViewAnimationOptions)curve;
return opt << 16;
}
The caveat here, as noted by Noah also, is that if Apple ever changes the enumerations where the two types no longer correspond, then this function will break. The reason to use it anyway, is that the switch based option doesn't work in all situations you may encounter today, while this does.
iOS 10+
Swift 5
A Swift alternative to converting UIView.AnimationCurve to UIView.AnimationOptions, which may not even be possible, is to use UIViewPropertyAnimator (iOS 10+), which accepts UIView.AnimationCurve and is a more modern animator than UIView.animate.
Most likely you'll be working with UIResponder.keyboardAnimationCurveUserInfoKey, which returns an NSNumber. The documentation for this key is (Apple's own notation, not mine):
public class let keyboardAnimationCurveUserInfoKey: String // NSNumber of NSUInteger (UIViewAnimationCurve)
Using this approach, we can eliminate any guesswork:
if let kbTiming = notification.userInfo?[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber, // doc says to unwrap as NSNumber
let timing = UIView.AnimationCurve.RawValue(exactly: kbTiming), // takes an NSNumber
let curve = UIView.AnimationCurve(rawValue: timing) { // takes a raw value
let animator = UIViewPropertyAnimator(duration: duration, curve: curve) {
// add animations
}
animator.startAnimation()
}

Circular NSSlider with stop (non-continuous) [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I am not sure how to phrase this better as a title but I need to make an NSSlider that functions as a normal volume knob. At the moment it will spin around as many times as I hold the mouse down and move it around the control. I need it to stop at the "0" position and the "100" position, I cannot have it jumping from 0 to 100 when I drag it the other way. I hope I am making this clear. Does anyone know how to do this or have any suggestions?
An old question, but here we go anyway:
(1) Set the slider to have 100 tickmarks
(2) Select "Only stop at tickmarks"
(3) In Interface Builder, set the custom cell class to SBCustomSliderCell
Use the code below:
#interface SBCustomSliderCell()
#property CGFloat lastSliderValue;
#end
#implementation SBCustomSliderCell
- (void)awakeFromNib
{
self.lastSliderValue = -1;
}
- (double)closestTickMarkValueToValue:(double)value
{
double proposedValue = [super closestTickMarkValueToValue:value];
if (self.lastSliderValue == -1)
{
self.lastSliderValue = proposedValue;
return proposedValue;
}
double MAX_JUMP = 50;
double tickDifference = ABS(self.lastSliderValue - proposedValue);
BOOL isTurningUp = proposedValue > self.lastSliderValue;
if (tickDifference > MAX_JUMP)
{
proposedValue = isTurningUp ? 0.0 : self.maxValue;
}
NSLog(#"value: %.2f gap: %.2f", proposedValue, tickDifference);
self.lastSliderValue = proposedValue;
return proposedValue;
}
- (NSRect)rectOfTickMarkAtIndex:(NSInteger)index
{
return NSZeroRect;
}
#end
I think you'll need to subclass NSSliderCell and override startTrackingAt:inView:, continueTracking:at:inView: and stopTracking:at:inView:mouseIsUp:.
The documentation of continueTracking:at:inView: says:
This method is invoked in trackMouse:inRect:ofView:untilMouseUp:. The default implementation returns YES if the cell is set to continuously send action messages to its target when the mouse button is down or the mouse is being dragged. Subclasses can override this method to provide more sophisticated tracking behavior.
Here's how to do it in Swift
This answer is based on Mark's "SBSLiderCell" answer above, but adapted for Swift.
Because it's a subclass of NSSliderCell, be sure to set the custom class in Interface Builder for the cell, and not the NSSlider superview.
Set the number of Tick Marks to your desired granularity (e.g.: 100) and click the "Only stop on tick marks" checkbox.
class PraxSliderCell : NSSliderCell {
var lastSliderValue: Double = -1
override func closestTickMarkValueToValue(value: Double) -> Double {
var proposedValue = super.closestTickMarkValueToValue(value)
if lastSliderValue == -1 { lastSliderValue = proposedValue }
else {
let tickDifference = abs(lastSliderValue - proposedValue)
let isTurningUp = proposedValue > lastSliderValue
if tickDifference > Double(numberOfTickMarks / 2) {
proposedValue = isTurningUp ? 0.0 : maxValue }
print (NSString(format: "value: %.2f gap: %.2f", proposedValue, tickDifference))
lastSliderValue = proposedValue
}
return proposedValue
}
override func rectOfTickMarkAtIndex(index: Int) -> NSRect { return NSZeroRect }
}