I am working in between three files: Menu.swift, Main.swift and Game.swift.
In my Main.swift, I define the variable swipeNumber:
class Main {
var swipeNumber: Int = 0 {
didSet{
println("The new swipe number is \(swipeNumber)")
}
}
}
N.B. It is in a class so that I can reference the variable from other files, and the didSet property observer will function.
As you can see, its initial value (I think) is 0.
Then, in my Menu.swift, I retrieve the information from the Main class in Main.swift.
let main = Main()
I then have three buttons, which will, on touch, change the swipeNumber variable, based on which button was pressed.
class Menu: UIViewController {
#IBAction func pressedThreeSwipes(sender: AnyObject) {
main.swipeNumber = 3
}
#IBAction func pressedFiveSwipes(sender: AnyObject) {
main.swipeNumber = 5
}
#IBAction func pressedTenSwipes(sender: AnyObject) {
main.swipeNumber = 10
}
//...
}
When I run the program, my property observer appears to work, printing messages such as:
The new swipe number is 3
The new swipe number is 5
The new swipe number is 10
And in the Game class, (for troubleshooting purposes), I have another property observer, checking the integer of the variable swipeNumber when the button test is pressed:
class Game: UIView {
let main = Main()
func didMoveToView(view: UIView) {
/* Setup your scene here */
println("now")
println("\(main.swipeNumber)"
//Nothing happens here, suggesting that didMoveToView is failing
}
#IBAction func test(sender: AnyObject) {
println("\(main.swipeNumber)")
}
}
My func test prints a number, but sadly that number is not 3, 5, or 10. It's 0.
I think that the problem lies with my variable in Main.swift, however I am not sure.
Any advice or 'fixes', whether quick or lengthy, would be very greatly appreciated.
Thank you,
Will
You have different instances of your class Main, and they each carry a different value for the same properties.
You should try the Singleton pattern (see e.g. here or here).
When you call Main(), you are creating a new object...emphasis on NEW. It has no knowledge of what you've done to other objects of the same type. If you want to use the same object in different places, you need to make it a parameter and pass it into methods rather than creating a different object.
Related
I have an UIViewController with 4 UIButtons. A user can tap any of those UIButtons and an UIView pops up. I want to add an didAppear() and didDisappear() function on the classes which are holding the UIViews depending on the users action. How can I call didDisappear() without the use of an enum, for example:
func didDisappear(view: EnumViews){
switch view{
case view0: myClassWithView0.didDisappear()
case view1: myClassWithView1.didDisappear()
case view2: myClassWithView2.didDisappear()
case view3: myClassWithView3.didDisappear()
}
}
Now I get 4 times duplicate data. I know that function exists for my class with a UIView, but how to call it? I made a protocol:
protocol ViewProtocol{
func didAppear()
func didDisappear()
}
I made the classes which are holding the UIView's conform to that protocol. However I do not know how to use it, when I create the class I get the error:
'myClassWithUIView' cannot be constructed because it has no accessible
initializers
The classes are all in an array and I can identify which UIView needs to pop up from the sender.tag. Ideally, I want to have something like this:
#IBAction func bringNewUIView(_ sender: UIButton) {
let newView = myArrayOfClassesWithUIView[sender.tag]
newView.didAppear()
}
You've got many things going on here. I'll start with the easy one.
'myClassWithUIView' cannot be constructed because it has no accessible initializers
This just means you don't have an initializer for your class. So inside your myClassWithUIView implementation you need to have init. I can't really help you with building the init because I don't know how that class is structured, but I will assume this is something you know how to do anyway.
Your #IBAction seems fine. Once you have an array of your classes that seems like it should work. Edit your post if that is not the case.
Finally, for your didDisappear question, you can do something like this:
func didDisappear(view: EnumViews) {
//Check to see if this view conforms to your ViewProtocol (that's not a good name, btw)
if let myClass = view as? ViewProtocol {
//Since it does conform to ViewProtocol you can call didDisappear on it
myClass.didDisappear()
}
}
Alternatively, if you already know that the didDisappear function is always passing in a view that conforms to ViewProtocol why not just change the argument and make that easier?
func didDisappear(view: ViewProtocol) {
view.didDisappear()
}
Is it possible to access and run a specific method/function from another class that can change dynamically as the app is run?
I’ll try to simplify the problem as much as possible.
SelectionPage.swift
Choose which class needs to be selected and accessed using an UIPickerView - 10 possible selections (Class1, Class2, Class3,…, Class10).
Class1.swift, Class2.swift, … Class10.swift
Each of the 10 classes has a single method that has exactly the same name but is programmed differently:
func runOnUpdate() { }
GameSceneViewController.swift
When a selection is made on the SelectionPage, the app segues to a GameSceneViewController where the specific selected function is run every time the update function is run:
override func update(currentTime: CFTimeInterval)
{
// run runOnUpdate() function here from selected class
}
Inside the update function, I would like to execute the runOnUpdate( ) function depending on which class was selected on the SelectionPage. Is this possible? Ideally I'd like to be able to assign the specific class/method in the:
override func didMoveToView(view: SKView)
so that I can access in other functions as well.
I’ve looked into lazy instantiation, creating delegates for each of the classes, #objc(Class1), arrays of [AnyClass], typealias, global variables in structs, singletons etc. but I’m unable to figure out how to make this work.
It seems like a fairly common problem so any help would be greatly appreciated! Thank you in advance!
You were correct in trying delegates as this is a case where you should make a protocol and a delegate. The protocol requires the function. From there you set the delegate property to an instance of a class that conforms to that protocol and then you call delegate?.someFunction() to call the function on the given object.
class ViewController: UIViewController {
var delegate: Updatable?
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
delegate = foo
delegate?.runOnUpdate() // prints do something
}
}
protocol Updatable {
func runOnUpdate()
}
class Foo: NSObject, Updatable {
func runOnUpdate() {
println("do something")
}
}
I am using a simple function to call a random image when a view controller is loaded.
override func viewDidLoad() {
super.viewDidLoad()
var randomtest = ReturnRandom()
mainImage.image = UIImage(named: randomtest)
}
I want a button's action to be based on what image was displayed. For example
#IBAction func Button1(sender: AnyObject) {
switch randomtest {
case 1: //Do something here
case 2: //Do something different here
default:
}
However I can not figure out how to get the value of "randomtest" out of ViewDidLoad. I also can not seem to create the random number outside the ViewDidLoad then pass the variable in. Sorry I'm new to all this, iOS development is a long way away from php...
The reason I was having trouble declaring the result of my function as a instance variable is that my function is also an instance function. I can not call it, there has been no instance. So this
class ViewController : UIViewController {
var randomtest: Int = ReturnRandom();
Will return a "Missing argument for parameter #1 in call"
For more details please check out this very helpful thread on setting initial values. Since my function was so simple I just computed the property while setting the initial value, no need for an additional class level function.
dynamic var randomtest:String {
let imageNumber = arc4random_uniform(3)
var imageString = String(imageNumber)
return (imageString)}
Hope this helps someone.
I have a major issue that I am stuck with at work and would REALLY appreciate some help on. This has cost me 2 days already.
What I am trying to do is have a special image class fire it's assigned callback when it's touched. Thats it.
But when I touch the image it crashes, often without an error message just (lldb). Sometimes it says garbage like "[__NSCFData tapped:]: unrecognized selector sent to instance 0x1780af360". Sometimes it says "message sent to deallocated object".
I can run the app 10 times in a row and get one of these random messages just from tapping the same object on the screen each time.
The code is very simple:
//view controller
var k:K_PreviewImage!
override func viewDidLoad()
{
var image:iImage = iImage(imageName: "ja3.jpg")
k = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
And here is the code inside K_PreviewImage (my clickable image). This does NOT inherit from anything, including NSObject
var touchCallback:((K_PreviewImage)->Void)
{
set{
if(_touchCallback == nil)
{
var tap:UIGestureRecognizer = UITapGestureRecognizer(target: self, action:"tapped:")
_image.addGestureRecognizer(tap)
}
_touchCallback = newValue
}
get{
return _touchCallback
}
func tapped(tap:UITapGestureRecognizer)
{
println("here")
if(_touchCallback != nil)
{
touchCallback(self)
}
}
The above code causes a crash 100% of the time.
If I add #objc too the tap event listener in the K_PreviewImage like this
#objc func tapped(tap:UITapGestureRecognizer)
{
println("here")
if(_touchCallback != nil)
{
touchCallback(self)
}
}
Then the code WORKS and the touch event is fired (both inside K_PreviewImage and the controllers callback function 'nestedTap'.
Not sure why that works, but it does. However I'm still up a creek without a paddle because when I make the K_PreviewImage a function variable instead of a class member variable, I get a crash again
override func viewDidLoad()
{
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
}
So my first question is why did I need to add #objc to get the callback to fire instead of giving me an unclear 'deallocated' memory crash?
My second question is why is it when I moved the variable from a member variable to a function variable does it cause the crash all over again. If the object was deallocated it couldnt hear the tap event in the first place could it? Why would it still be on screen? And why is it getting dealloacted just because it's not a member variable!
How the heck can I create a custom object dynamically and have it fire click events!
UPDATE
Here is the full code block involving how my K_PreviewImage was being added with a touch event
var _array:Array<iImage>!
override func viewDidLoad()
{
_array = Array<iImage>()
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
_array.append(k.image)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
Solved:
override func viewDidLoad()
{
_array = Array<K_PreviewImage>()
var image:iImage = iImage(imageName: "ja3.jpg")
var k:K_PreviewImage = K_PreviewImage(image: image)
k.touchCallback = nestedTap
_view.addSubview(k.image)
_array.append(k)
}
func nestedTap(k:K_PreviewImage)
{
println("Successs")
}
Because I was not storing a reference to the K_PreviewImage 'k' even though the subview was added, even though it belongs to k, k was not being retained. By making the array store a reference to K_PreviewImage instead of iImage the compiler now retains a reference to the K_Preview Image
First off, it looks like you're trying to add an image as a subview. I don't know Swift all that well, but _view.addSubview(k.image) looks wrong to me and would make more sense as _view.addSubview(k).
Secondly, you're not keeping your reference to your K_PreviewImage. You are assigning k.image as the subview, which causes k to be detected as "no longer used" by the system. It will clean it up and when you try to access it, you'll crash.
I read a few tutorial about swift, and have made simple counter app, but I want modify it to leave controller clean and logic move to external class:
In ViewController.swift i have
class ViewController: UIViewController {
var counter: Counter?
override func viewDidLoad() {
super.viewDidLoad()
counter = Counter(label: labelCounter)
counter.renderInit()
}
}
and I have Counter class:
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
...
}
}
Unfortunatelly in controller on line counter.renderInit() I see error message:
'Counter?' does not have a member named 'renderInit'
Change counter.renderInit() to one of these:
counter?.renderInit()
counter!.renderInit()
Counter? is an optional type. You need to unwrap it. Doing ? will ignore the method if counter is nil, and ! will force it to unwrap and throw an error if it does not exist.
Check out this page in Swift's documentation for more on optionals.
ok, I move step forward and have another problem with call method object.
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
label?.text = String(counter)
}
}
I got "Cannot assign to the result of this expression." (for line where I want assign number converted to string, to label text.
I read documentation and couple of tutorials, but with learning new thing it is good to look on example and to it in similar way. Unfortunatelly Swift is young language and there are not many examples. (I am Ruby language programmer).