I have a view with a delegate that I want to have call numpadView(numpadView:) in GameController upon button press, however I can't get it to work. The overload of touchesBegan() works fine, it's really the line with pressDelegate?.numpadView(self) which doesn't call the delegate function in the GameController class. I'm stumped as to what's missing to get it to work?
I cleaned the code to leave only the essential related to the problem for simplicity.
NumpadView.swift
protocol NumpadPressDelegateProtocol {
func numpadView(numpadView: NumpadView)
}
class NumpadView: UIButton{
var pressDelegate: NumpadPressDelegateProtocol?
init(char: Character) {
let frame = CGRectMake(160, 100, 50, 50)
super.init(frame:frame)
self.setTitle("\(char)", forState: UIControlState.Normal)
self.userInteractionEnabled = true
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
pressDelegate?.numpadView(self)
}
}
GameController.swift
class GameController: NumpadPressDelegateProtocol {
func numpadView(numpadView: NumpadView) {
//do something
}
}
Declaring GameController as NumpadPressDelegateProtocol is not enough for you to get a callback. You also need to set the pressDelegate of the NumpadView instance inside the Gamecontroller. Assuming numpadView is the instance variable be it IBOutlet or not, you have to set the delegate like
Inside GameController init
numpadView.pressDelegate = self
Have you set the pressDelegate property to something? Nothing is assigned to it in the code you've shown. Do you assign something to it elsewhere in your code?
Related
I want to extend UIView by adding some functions, and override them in any subclass of UIView that I want. I found in apple documentations that I can't override extensions (and the compiler will complain) which make some sense. So
I need someone to suggest an alternative way to the below:
extension UIView {
func hide() { //do almost nothing }
}
class myLabel: UILabel {
override func hide() {
//do work on uilabel that can't be done on imgView
}
}
class myImageView: UIImageView {
override func hide() {
//do work on imgView that can't be done on uilabel
}
}
And the reason I want this is that later in my code I will face the below code and I have to many subclasses and I don't want to write too many if-lets trying to cast the view to myLabel, myTextView, myImageView... etc
let view = cell.viewWithTag(someTag)
// and I want to write this below without casting
view.hide()
I tried with protocols and protocol extensions but I couldn't make it though.
Any thoughts?
Note: func hide() is just an example. My func will have more to do.
**Question updated to be clear.
EDIT: Updating answer to make use of protocols also
Protocols does in various ways enable to you replace subclassing in some cases however you still need your class to conform to the protocol to be able to see and override those methods
You can have a protocol for example:
protocol SomeProtocol {
func hide()
}
To do what you are intending to do it is best to have a parent subclass UIView with all functions that can be overridden for example (in this updated answer you can have your methods to override inside the protocol and have your subclasses conform to it):
class ParentView : UIView, SomeProtocol {
func hide() {
print("PARENT")
}
func anyOtherMethod() {
}
}
and then have all the other UIView's that need to override those methods subclass ParentView:
class ViewOne : ParentView {
override func hide() {
print("VIEW ONE")
}
}
class ViewTwo : ParentView {
override func hide() {
print("VIEW TWO")
}
}
So even if you later place this code:
let view = cell.viewWithTag(someTag)
// and I want to write this below without casting
view.hide()
you won't need to explicitly cast your UIView's, the view will call it's intended overridden method, unless and until you call super in your overridden method also
EDIT: More on making use of protocols
In the case you need other controls to also have a hide() method to override then you can still have to subclass, for example in the case of UILabel you need to override it:
class ParentLabel : UILabel, SomeProtocol {
func hide() {
print("PARENT LABEL")
}
}
then you can write the intended code with casting to your protocol
if let view = cell.viewWithTag(someTag) as? SomeProtocol {
view.hide() // prints PARENT LABEL
}
and either use that subclassed UILabel control or if you need in some cases some label to override that behavior then you can still create a child subclass of ParentLabel:
class LabelOne : ParentLabel {
override func hide() {
print("LABEL ONE")
}
}
everyone knows that when you drag outside a button it don't cancel the highlight state right away by UIButton's default. UIControlEventTouchDragExit triggers when 70 pixels away. I want that distance to be 0. So after searching the solution of it, I tried to create a subclass like this:
import UIKit
class UINewButton: UIButton {
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
print("here")
let touchOutside = !CGRectContainsPoint(self.bounds, touch.locationInView(self))
if touchOutside {
let previousTochInside = CGRectContainsPoint(self.bounds, touch.previousLocationInView(self))
if previousTochInside {
print("Sending UIControlEventTouchDragExit")
self.sendActionsForControlEvents(.TouchDragExit)
self.highlighted = false
self.selected = false
}else{
print("Sending UIControlEventTouchDragOutside")
self.sendActionsForControlEvents(.TouchDragOutside)
}
}else{
let previousTouchOutside = !CGRectContainsPoint(self.bounds, touch.previousLocationInView(self))
if previousTouchOutside{
print("Sending UIControlEventTouchDragEnter")
self.sendActionsForControlEvents(.TouchDragEnter)
}else{
print("Sending UIControlEventTouchDragInside")
self.sendActionsForControlEvents(.TouchUpInside)
}
}
return super.continueTrackingWithTouch(touch, withEvent: event)
}
}
and create a button like this in a UIViewController
#IBOutlet var confirmButton: UINewButton!
I assumed when a UIButton being touched and dragged. It would call the function in this sequence:
beginTrackingWithTouch(when touched) -> continueTrackingWithTouch(when dragged) -> endTrackingWithTouch(when left)
But here is the weird part. Even though I override the function continueTrackingWithTouch, it still not been called. Cause the console window didn't show "here" where I put there in it. And the result remain the default distance 70. how come is that?
I tried to call the three functions mentioned above and return true if it needs one.
What did I missed?
After reading this article: UIControlEventTouchDragExit triggers when 100 pixels away from UIButton
Still not helping :( (plus it written in objective-C...)
Isn't the distance of 70px a property of the function so I can just changed?(How can I see the original function by the way? There is no detail in Apple Developer Documentation...)
Should I use button.addtarget in the UIViewController? But it seems like another way to do it.
Here is another question:
If I want to cancel the highlight state when dragged outside the button, is this right?
self.highlighted = false
self.selected = false
I don't know which one is the right one so I used it all.
please help! Just a newbie in swift but I have been stuck in this problem for 3 days. QQ
In Swift 3 the function signature has changed. It's now:
func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool
API Reference
I'm trying to create a subclass of SKLabelNode for a button in SpriteKit. I tried to create a button using SKLabelNode as a parent, so I can use everything I know about labels while creating my buttons (font, text size, text color, position, etc).
I've looked into Swift Spritekit Adding Button Programaticly and I'm using the basis of what that is saying, but rather I'm making a subclass instead of a variable, and I'm creating the button using a label's code. The subclass has the added function that will allow it to be tapped and trigger an action.
class StartScene: SKScene {
class myButton: SKLabelNode {
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if myButton.containsPoint(location) {
// Action code here
}
}
}
}
override func didMoveToView(view: SKView) {
let startGameBtn = myButton(fontNamed: "Copperplate-Light")
startGameBtn.text = "Start Game"
startGameBtn.fontColor = SKColor.blackColor()
startGameBtn.fontSize = 42
startGameBtn.position = CGPointMake(frame.size.width/2, frame.size.height * 0.5)
addChild(startGameBtn)
}
}
My error is in: if myButton.containsPoint(location)
The error reads: Cannot invoke 'containsPoint' with an argument list of type '(CGPoint)'
I know it has to do something with my subclass, but I have no idea what it is specifically.
I also tried putting parentheses around myButton.containsPoint(location) like so:
if (myButton.containsPoint(location))
But then the error reads: 'CGPoint' is not convertible to 'SKNode'
Thanks
I have an alternative solution that would work the same way.
Change the following code
if myButton.containsPoint(location) {
To this and it should compile and have the same functionality
if self.position == location {
I have a UIViewController with a custom UIGestureRecognizer added to one of its views. The problem is that i get all the touch printlines from my DialGestureRecognizer but the handleDial action is never called. (iOS 8 / Xcode 6.1.1) What did i do wrong? Any help appreciated.
#IBOutlet var dialView:UIView?
override func viewDidLoad() {
super.viewDidLoad()
if let _dialView = dialView? {
let recognizer = DialGestureRecognizer(target:self, action:"handleDial:")
_dialView.addGestureRecognizer(recognizer)
}
func handleDial(recognizer:DialGestureRecognizer) {
println("handleDial")
}
}
The custom (stripped down) UIGestureRecognizer looks like this
class DialGestureRecognizer:UIGestureRecognizer {
func touchesBegan(touches:NSSet!, withEvent event:UIEvent!) {
println("touchesBegan")
}
func touchesMoved(touches:NSSet!, withEvent event:UIEvent!) {
println("touchesMoved")
}
func touchesEnded(touches:NSSet!, withEvent event:UIEvent!) {
println("touchesEnded")
}
}
You have to trigger the selector from inside the gesture recogniser.
To trigger the selector, you have to modify the state property of the UIGestureRecognizer. This property is readonly.
For modifying that you have to add a BridgingHeader as follows.
You need to have or create a -Bridging-Header.h file to import Objective C headers such as the one you want.
If you don't already have a bridge header file in your app, the easiest way to get one is to add an objc class to your project, and xcode will ask if you want one, then creates the file and ties it into the settings for you. You can then delete the objc class.
Add the next line to the Bridging Header file.
#import <UIKit/UIGestureRecognizerSubclass.h>
Then you can modify the self.state property to get the events fired.
override func touchesBegan(touches:NSSet!, withEvent event:UIEvent!) {
println("touchesBegan")
self.state = UIGestureRecognizerState.Began
}
For more information read
http://www.raywenderlich.com/76020/using-uigesturerecognizer-with-swift-tutorial
I am adding a UIView to the view of an SKScene. Later, when I wish to remove that UIView form its superview, using the standard method of uiview.removeFromSuperview does not seem to work. How should I be accomplishing this instead? Here is how I add the UIView:
func addContainerView() {
let containerRect = CGRectMake(400, 24, 600, 720)
smallerView = UIView(frame: containerRect)
smallerView.backgroundColor = UIColor.redColor()
self.view.addSubview(smallerView)
}
Here is how I am attempting to remove it:
func removeContainerView() {
smallerView.removeFromSuperview()
}
This all takes place within the SKScene class, so here 'self' refers to that scene.
Any thoughts?
First of all I wonder which version of swift your are using.
self.view is optional in 1.2
hence your should type: self.view?.addSubview() if you are targeting swift 1.2
I have tried in swift 1.2 to make a simple app
class GameScene: SKScene {
let subview = UIView()
override func didMoveToView(view: SKView) {
subview.frame = CGRectMake(0, 0, 100, 100)
subview.backgroundColor = SKColor.orangeColor()
self.view?.addSubview(subview)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
removeContainerView()
}
func removeContainerView() {
subview.removeFromSuperview()
}
}
The above code works very well.
I can think of a couple of reasons your view doesn't get removed
Are you sure that removeContainerView is actually called. Try to make a break point to see if it's called
If you have set up your SKView in code something might have been setup wrong.
Your subview is being deallocated or something
To fully debug your problem we need to see some more code.
What we need is:
Declaration of your subview
All functions that call removeContainerView()
Even better would be to pastebin your SKScene class.