Touch Slide over UIButton issue - objective-c

Can someone tell me if I have translated the first 2 lines correctly to Swift and if the first part is correctly? Also, could anyone help me figure out the rest. I can't figure out how to translate the if statement at the bottom..
[C addTarget:self action:#selector(outsideOfKey: forEvent:) forControlEvents:UIControlEventTouchDragOutside|UIControlEventTouchDragInside];
[C addTarget:self action:#selector(keyGetsLeft: forEvent:) forControlEvents:UIControlEventTouchUpOutside | UIControlEventTouchUpInside];
-(void) outsideOfKey:(id)sender forEvent:(UIEvent *)event
{
for(UITouch *t in [event allTouches])
{
CGPoint touchPoint = [t locationInView:window];
if(CGRectContainsPoint(C.frame, touchPoint))
{
C.highlighted = YES;
}
else{
C.highlighted = NO;
}
Translated to swift
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragOutside)
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragInside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpOutside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpInside)
func outsideOfKey (sender: AnyObject, forEvent: UIEvent) {
let touch = event.allTouches() as? UITouch
for touch
{
var touchPoint : CGPoint = touch.locationInView(window)
if(CGRectContainsPoint(C.frame, touchPoint))
{
C.highlighted = YES;
}
else{
C.highlighted = NO;
}
}

Try something like:
C.addTarget(self, action:Selector("outsideOfKey:forEvent:"), forControlEvents:.TouchDragOutside | .TouchDragInside)
C.addTarget(self, action:Selector("keyGetsLeft:forEvent:"), forControlEvents:.TouchUpOutside | .TouchUpInside)
func outsideOfKey(sender: AnyObject, forEvent event: UIEvent) {
if let touches = event.allTouches()?.allObjects as? [UITouch] {
for touch in touches {
var touchPoint : CGPoint = touch.locationInView(window)
if CGRectContainsPoint(C.frame, touchPoint) == true {
C.highlighted = true;
} else {
C.highlighted = false;
}
}
}
}
Points:
You can still use the "|" operator on enums in swift (most of the time)
You don't put parentheses around the conditional clause in swift
YES and NO are not valid in swift, you much use true and false
Swift does not support sets so it is easiest to get the touches as an Array so you can iterate using a simple swift for...in loop
The "if let ..." pattern is called optional binding, if the right hand side is not nil "touches" will be set to the value and the code in the following braces will be executed, otherwise it will skip the block of code
The way "?" is used after ".allTouches()" is called optional chaining, if .allTouches() returns nil, the whole expression will return nil
Hope this is of some use to you, let me know if you have any more swift queries!

Related

Bindings does not update NSTextField

I use bindings to NSObjectController within XIB. When I set new content object of NSObjectController the only textfield value which doesn't change is the one that has first responder. Model changes without an issue.
If I don't use custom getter/setter the textfield that has firstResponder (isBeingEdited) changes without an issue.
What's wrong with my KVC, KVO?
My custom getter/setter is below pic.
PS: I don't want to make window a first responder before I change content object to make it work.
static const CGFloat MMsInOneInch = 25.4;
static const CGFloat inchesInOneMM = 0.0393700787402;
- (void)setPaperWidth:(CGFloat)paperWidth
{
[self willChange];
CGFloat newWidth = paperWidth * [self conversionKoeficientToDefaultUnitType];
if (!_isChangingPaperSize) {
if (self.paperSize == PPPaperSizeA4 && fabs(newWidth - widthSizeOfA4) > 0.001) {
[self setPaperSize:PPPaperSizeCustom];
}
if (self.paperSize == PPPaperSizeUSLetter && fabs(newWidth - widthSizeOfUSLetter) > 0.001 ) {
[self setPaperSize:PPPaperSizeCustom];
}
}
[self willChangeValueForKey:#"paperWidth"];
_paperWidth = newWidth;
[self didChangeValueForKey:#"paperWidth"];
[self didChange];
}
- (CGFloat)conversionKoeficientToDefaultUnitType
{
if ([self defaultUnitType] == [self unitType]) {
return 1;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeMM) {
return MMsInOneInch;
}
if ([self defaultUnitType] == PPPrintSettingsUnitTypeInch) {
return inchesInOneMM;
}
return 1;
}
- (CGFloat)paperWidth
{
return _paperWidth / [self conversionKoeficientToDefaultUnitType];
}
I forgot that I use NSNumberFormatter with min/max value which where blocking NSTextField to update.

Two finger swipe in Yosemite 10.10

I have been using a similar method to as this:
https://github.com/oscardelben/CocoaNavigationGestures
To capture two finger swipes on the Mac, under Yosemite it is no longer working. Anyone know what has change, or what I need to change for this to work.
The accepted answer didn't work well for me - it would often not detect the swipe. Instead, I overrode wantsScrollEventsForSwipeTrackingOnAxis:(NSEventGestureAxis)axis to return YES for the appropriate axis, then overrode scrollWheel:(NSEvent *)theEvent to detect scrolling. Works perfect every time.
The complete answer in Swift 5.3, based on #bmuller would be:
override func wantsScrollEventsForSwipeTracking(on axis: NSEvent.GestureAxis) -> Bool {
return axis == .horizontal
}
override func scrollWheel(with event: NSEvent) {
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
Multiple swipe events might be sent in a single gesture with this code. You may need to add phase(NSEventPhase) handling code to the scrollWheel(...) function e.g.
override func scrollWheel(with event: NSEvent) {
guard event.phase == .began else {
return
}
if event.scrollingDeltaX < 0 {
print("Go forward")
}
else {
print("Go back")
}
}
On your Mac, go to system preferences and you will find all the different settings of the trackpad.
From here: https://discussions.apple.com/thread/5710582
Hope I helped.
This was my solution, seems to be working for me.
#define kSwipeMinimumLength 0.2
- (void)touchesBeganWithEvent:(NSEvent *)event{
if(event.type == NSEventTypeGesture){
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseAny inView:self];
if(touches.count == 2){
self.twoFingersTouches = [[NSMutableDictionary alloc] init];
for (NSTouch *touch in touches) {
[self.twoFingersTouches setObject:touch forKey:touch.identity];
}
}
}
}
- (void)touchesMovedWithEvent:(NSEvent*)event {
NSSet *touches = [event touchesMatchingPhase:NSTouchPhaseEnded inView:self];
if(touches.count > 0){
NSMutableDictionary *beginTouches = [self.twoFingersTouches copy];
self.twoFingersTouches = nil;
NSMutableArray *magnitudes = [[NSMutableArray alloc] init];
for (NSTouch *touch in touches)
{
NSTouch *beginTouch = [beginTouches objectForKey:touch.identity];
if (!beginTouch) continue;
float magnitude = touch.normalizedPosition.x - beginTouch.normalizedPosition.x;
[magnitudes addObject:[NSNumber numberWithFloat:magnitude]];
}
float sum = 0;
for (NSNumber *magnitude in magnitudes)
sum += [magnitude floatValue];
// See if absolute sum is long enough to be considered a complete gesture
float absoluteSum = fabsf(sum);
if (absoluteSum < kSwipeMinimumLength) return;
// Handle the actual swipe
// This might need to be > (i am using flipped coordinates), you can use an else to go forward also.
if (sum > 0){
NSLog(#"go back");
}
}
}

Objective C - Change System Font with Category

I want to change the default font for all UITextViews. It seems that the easiest way to do this is via custom category. I found this solution: Change the default systemFont used by controls and tried to implement it.
But my UITextViews are added programmatically so the awakeFromNib function is not called. I tried to move it to initWithFrame like this:
-(id)initWithFrame:(CGRect)frame
{
id result = [super initWithFrame:frame];
if (result) {
float size = [self.font pointSize];
NSString *stringfontstyle=self.font.fontName;
if([stringfontstyle rangeOfString:#"Bold"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Black" size:size];
}
else if ([stringfontstyle rangeOfString:#"Italic"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Oblique" size:size];
}
else if ([stringfontstyle rangeOfString:#"Medium"].location != NSNotFound) {
self.font = [UIFont fontWithName:#"Avenir-Medium" size:size];
}
else {
self.font = [UIFont fontWithName:#"Avenir-Roman" size:size];
}
}
return result;
}
Weird is that if my category contains initWithFrame function, the UITextView disappears. What is it that I'm missing?
Note: I'm using autoLayout so the initWithFrame is called with CGRectZero, but I suppose that isn't the problem.
EDIT:
The problem is that the font is null when the UITextView is initiated. So what method would be appropriate to place the code into?
when category contains a method, it overrides the class's method... and thats not good. subclassing would work.. method swizzling might be a way but...
why don't you just subclass UITextView - then you can keep your initWithFrame thingy or maybe override font
- (UIFont*)font {
if(!myFont) {
_myFont = xy;
}
id superFont = super.font;
if(![superFont.name isEqualTo:_myFont.name]) {
super.font = [myFont fontWithSize:superFont.pointSize];
}
return _myFont;
}
or setFont:
- (void)setFont:(UIFont*)newFont {
if(!myFont) {
_myFont = xy;
}
id thisFont = [_myFont fontWithSize:newFont.pointSize];
super.font = thisFont;

How Can I get keyboard input in a SpriteKit Game?

I'm a beginner in SpriteKit programming, and have been trying to figure out how to handle input from the keyboard.
What I've found so far is that you should subclass NSResponder and implement it like this:
#interface AppDelegate : NSResponder <NSApplicationDelegate>
-(void)keyUp:(NSEvent *)theEvent;
-(void)keyDown:(NSEvent *)theEvent;
#end
#implementation AppDelegate
-(void)keyUp:(NSEvent *)theEvent
{
NSLog(#"Key Released");
}
-(void)keyDown:(NSEvent *)theEvent
{
NSLog(#"Key Pressed");
}
#end
Obviously, there are a few more methods/properties in the interface and implementation of AppDelegate but I didn't put them there to keep the question relevant.
Next, I would start using key codes to detect which keys are being pressed, but the keyUp and keyDown methods don't even get called. I'm not sure why.
Any Help?
Update:
Thanks for your answers! I discovered that you have to implement keyUp and keyDown directly in your scene class, because they won't get called from the AppDelegate. Thanks again for the help!
The easiest way I know is to implement the keyDown method in your SKScene (and not directly in the AppDelegate). You don't have to subclass anything.
- (void)keyDown:(NSEvent *)event {
[self handleKeyEvent:event keyDown:YES];
}
- (void)keyUp:(NSEvent *)event {
[self handleKeyEvent:event keyDown:NO];
}
Then use the method handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp to check which key has been pressed :
- (void)handleKeyEvent:(NSEvent *)event keyDown:(BOOL)downOrUp {
// First check the arrow keys since they are on the numeric keypad.
if ([event modifierFlags] & NSNumericPadKeyMask) { // arrow keys have this mask
NSString *theArrow = [event charactersIgnoringModifiers];
unichar keyChar = 0;
if ([theArrow length] == 1) {
keyChar = [theArrow characterAtIndex:0];
switch (keyChar) {
case NSUpArrowFunctionKey:
self.defaultPlayer.moveForward = downOrUp;
break;
case NSLeftArrowFunctionKey:
self.defaultPlayer.moveLeft = downOrUp;
break;
case NSRightArrowFunctionKey:
self.defaultPlayer.moveRight = downOrUp;
break;
case NSDownArrowFunctionKey:
self.defaultPlayer.moveBack = downOrUp;
break;
}
}
}
// Now check the rest of the keyboard
NSString *characters = [event characters];
for (int s = 0; s<[characters length]; s++) {
unichar character = [characters characterAtIndex:s];
switch (character) {
case 'w':
self.defaultPlayer.moveForward = downOrUp;
break;
case 'a':
self.defaultPlayer.moveLeft = downOrUp;
break;
case 'd':
self.defaultPlayer.moveRight = downOrUp;
break;
case 's':
self.defaultPlayer.moveBack = downOrUp;
break;
case ' ':
self.defaultPlayer.fireAction = downOrUp;
break;
}
}
}
I took this code from the Apple SpriteKit Adventure game. I found it very usefull to learn SpriteKit :)
Swift (2.0) version of HeyFara's answer. I've just popped "breaks" in where you would make your actual function calls.
public override func keyDown(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: true)
}
public override func keyUp(theEvent: NSEvent) {
handleKeyEvent(theEvent, keyDown: false)
}
public func handleKeyEvent(event:NSEvent, keyDown:Bool){
if event.modifierFlags.contains(NSEventModifierFlags.NumericPadKeyMask){
if let theArrow = event.charactersIgnoringModifiers, keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters.characters{
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
Here is rougeExciter version of HeyFara's answer but in Swift 4.2... in case someone out there find this old post... as myself a few minutes ago :)...
public override func keyDown(with event: NSEvent) {
handleKeyEvent(event, keyDown: true)
}
public override func keyUp(with event: NSEvent) {
handleKeyEvent(event, keyDown: false)
}
public func handleKeyEvent(_ event: NSEvent, keyDown: Bool){
if event.modifierFlags.contains(NSEvent.ModifierFlags.numericPad) {
if let theArrow = event.charactersIgnoringModifiers, let keyChar = theArrow.unicodeScalars.first?.value{
switch Int(keyChar){
case NSUpArrowFunctionKey:
break
case NSDownArrowFunctionKey:
break
case NSRightArrowFunctionKey:
break
case NSLeftArrowFunctionKey:
break
default:
break
}
}
} else {
if let characters = event.characters{
for character in characters {
switch(character){
case "w":
break
default:
print(character)
}
}
}
}
}
If you want to know whenever a key is pressed, you should subscribe to the global event manager.
[NSEvent addGlobalMonitorForEventsMatchingMask:(NSKeyDownMask) handler:^(NSEvent *event) {
[NSEvent removeMonitor:event];
This will call the handler when ever a key is pressed.

Anyway to make a (wrapping) NSTextField write a carriage return upon pressing return key?

I want to use a wrapping text field that can potentially contain carriage returns in my app. Is there any way to force the NSTextField object to write a carriage return into the text area instead of sending its action to its target when the Return key is pressed?
This is covered in Technical Q&A QA1454, which also enumerates reasons why one would use NSTextField instead of NSTextView in this case.
You can implement the following method in the text field delegate:
- (BOOL)control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver
// to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
Okay, I figured out one way to do it, but this very well may not be the best (or even a good) way. I subclassed NSTextField, and overrode -textShouldEndEditing: like so:
-(BOOL)textShouldEndEditing:(NSText *)textObject {
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36) {
[self setStringValue:[[self stringValue] stringByAppendingString:#"\n"]];
return NO;
}
else {
return [super textShouldEndEditing:textObject];
}
}
I found a combination of Sean and Bevarious worked best for me. Sean's answer assumes that the new line is always wanted to be added to the end (instead of for instance where the user's cursor is placed).
-(BOOL)textShouldEndEditing:(NSText *)textObject
{
NSEvent * event = [[NSApplication sharedApplication] currentEvent];
if ([event type] == NSKeyDown && [event keyCode] == 36)
{
[textObject insertNewlineIgnoringFieldEditor:nil];
return NO;
}
else
{
return [super textShouldEndEditing:textObject];
}
}
Swift version:
override func textShouldEndEditing(textObject: NSText) -> Bool {
let event = NSApplication.sharedApplication().currentEvent
if event?.type == NSEventType.KeyDown && event?.keyCode == 36 {
self.stringValue = self.stringValue.stringByAppendingString("\n")
return false
} else {
return super.textShouldEndEditing(textObject)
}
}