next level button gives unknown error in swift - ios

I've a gameviewcontroller which gets 2 values from 1st view controller .. i.e. selectedlevelnumber and nextlevelnumber. I want to show Next Level button when current level is completed. So next level button is showing when level is completed but when clicking on next level button it gives me unknown error, console says lldb and takes me to AppDelegate.swift.
I have this code in my GameViewController
if levelisCompleted == true {
LevelCompletedView().levelcomplete(selectedlevelnumber, nextlevel: nextlevelnumber)
} .. this is working perfectly
now this is my LevelCompletedView
class LevelCompletedView: UIViewController {
func levelcomplete(levelnum:String, nextlevel:String) {
println("\(levelnum) is completed ")
println("next level is \(nextlevel)")
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(10, 10, 200, 50)
button.backgroundColor = UIColor.greenColor()
button.setTitle("Next Level", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonA:", forControlEvents: UIControlEvents.TouchUpInside)
gamesubView.addSubview(button)
func buttonA(sender:UIButton!) {
println("loading next level \(nextlevel)")
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var gameview = self.storyboard?.instantiateViewControllerWithIdentifier("gamevc") as GameViewController
gameview.levelSelectedNum = String(nextlevel)
self.presentViewController(gameview, animated: true, completion: nil)
}
}
}
everything works, only the button is not working when clicked.

As it stands now, the buttonA: method is local since it's contained within levelComplete, so once levelComplete has finished executing, the buttonA: function disappears. Your button's not recognizing it's target action buttonA: because it wasn't defined within the scope of its class -- thus the resulting error. Move func buttonA outside of levelComplete to correct this issue.
And to access nextlevel, save it as a global variables, ex.
class LevelCompletedView: UIViewController {
var nextLevelVar:String!
func levelcomplete(levelnum:String, nextlevel:String) {
println("\(levelnum) is completed ")
println("next level is \(nextlevel)")
nextLevelVar = nextlevel
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(10, 10, 200, 50)
button.backgroundColor = UIColor.greenColor()
button.setTitle("Next Level", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonA:", forControlEvents: UIControlEvents.TouchUpInside)
gamesubView.addSubview(button)
}
func buttonA(sender:UIButton!) {
println("loading next level \(nextLevelVar)")
let storyboard:UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var gameview = self.storyboard?.instantiateViewControllerWithIdentifier("gamevc") as GameViewController
gameview.levelSelectedNum = String(nextLevelVar)
self.presentViewController(gameview, animated: true, completion: nil)
}
}

Related

Dynamic Creation UIButton AddTarget on UIView executed on UIViewController

Currently this code that executes a Tag Styled List, The issue remains when I want to try to pass the addTarget Action optionClicked to my UIViewController
DrawerView.swift
let menuOptions = ["Info", "Actions", "Users", "Patiens"]
menuOptions.forEach({
let button = UIButton()
button.setTitle($0, for: .normal)
if $0.contains(menuOptions[0]) {
button.style(with: .filled)
} else {
button.style(with: .outlined)
button.addTarget(self, action: #selector(optionClicked(_:)), for: .touchUpInside)
}
actionStackView.addArrangedSubview(button)
})
DrawerController.swift
class DrawerController: UIViewController {
var shareView = DrawerView()
var viewModel: CarDetailViewModel?
override func loadView() {
shareView.viewModel = viewModel
view = shareView
}
#objc func optionClicked(_ sender: UIButton) {
let feedbackGenerator = UISelectionFeedbackGenerator()
feedbackGenerator.selectionChanged()
let optionClicked: String = sender.titleLabel?.text ?? "0"
switch optionClicked {
case "Actions": present(DrawerActionController(), animated: true, completion: nil)
case "Notifications":
let viewController = DrawerNotificationController()
viewController.carDetailViewModel = viewModel
present(viewController, animated: true, completion: nil)
case "Patients":
let viewUserController = DrawerUserController()
viewUserController.carPath = "9000"
present(viewUserController, animated: true, completion: nil)
default: break
}
}
}
Tried button.addTarget(self, action: #selector(optionClicked(_:)), for: .touchUpInside) but did not work out.
In the method button.addTarget(self, action: #selector(optionClicked(_:)), for: .touchUpInside) you need to provide pointer to the viewController which will receive the action.
The easiest way in your case would be to create lazy variable DrawerView with DrawerController on the init and use the drawer controller in the button action.
DrawerView.swift
class DrawerView: UIView {
private unowned let drawerController: DrawerController
init(drawerController: DrawerController) {
self.drawerController = drawerController
}
... wherever is your code placed ...
let menuOptions = ["Info", "Actions", "Users", "Patiens"]
menuOptions.forEach({
let button = UIButton()
button.setTitle($0, for: .normal)
if $0.contains(menuOptions[0]) {
button.style(with: .filled)
} else {
button.style(with: .outlined)
button.addTarget(drawerController, action: #selector(DrawerController.optionClicked(_:)), for: .touchUpInside)
actionStackView.addArrangedSubview(button)
}
})
....
}
It's important you use unowned or weak to prevent retain cycles and memory leaks
To create the DrawerView you can use a lazy variable:
lazy var shareView: DrawerView = DrawerView(drawerController: self)
lazy will allow you to use self, you can also use optional and create the variable later, but that's basically what lazy does.
Hope it helps!

ARC Memory Management Changing Per Example

Note: I created a Test program so I can better understand how ARC works - I am having trouble implementing it in my real project.
I created a Test program to determine how ARC works - it works great! Here it is.
ViewController1:
import UIKit
class ViewController: UIViewController {
weak var vc2:ViewController2?
override func viewDidLoad() {
super.viewDidLoad()
print("VC1 Initialized")
addButton()
}
func addButton() {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
button.backgroundColor = .red
button.setTitle("Go to VC2", for: .normal)
self.view.addSubview(button)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
#objc func buttonAction(sender: UIButton!) {
let vx = ViewController2()
vx.VC1 = self
vc2 = vx
self.present(vc2!, animated: false, completion: nil)
}
}
ViewController2:
import UIKit
class ViewController2: UIViewController {
weak var VC1:ViewController?
weak var VC3:ViewController3?
override func viewDidLoad() {
super.viewDidLoad()
print("VC2 Initialized")
addButton()
}
deinit {
print("VC2 Deinitialized")
}
func addButton() {
for i in 0..<2 {
let button = UIButton(frame: CGRect(x: 0, y: self.view.frame.height*0.5*CGFloat(i), width: self.view.frame.width, height: self.view.frame.height*0.5))
button.backgroundColor = i == 0 ? .red : .blue
button.setTitle("Go to VC\(i*2+1)", for: .normal)
self.view.addSubview(button)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.tag = i
}
}
#objc func buttonAction(sender: UIButton!) {
let tag = sender.tag
if(tag == 0) {
self.dismiss(animated: true, completion: nil)
}
else {
let vc3 = ViewController3()
vc3.ViewController2 = self
VC3 = vc3
self.present(VC3!, animated: true, completion: nil)
}
}
}
ViewController3:
import UIKit
class ViewController3: UIViewController {
var ViewController2:ViewController2?
override func viewDidLoad() {
super.viewDidLoad()
print("VC3 Initialized")
addButton()
}
deinit {
print("VC3 Deinitialized")
}
func addButton() {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
button.backgroundColor = .red
button.setTitle("Go to VC1", for: .normal)
self.view.addSubview(button)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
}
#objc func buttonAction(sender: UIButton!) {
self.ViewController2!.VC1!.dismiss(animated: true, completion: nil)
}
}
What I was looking at was how to remove multiple pages at a time (as indicated by the button push on VC3 and it goes back to the main page) and still remove ARC memory. All goes well. You can see the output below of me doing these operations.
Start program - push to page 2 - push to page. 1 - push to page 2 - push to page 3 - go to page 1
VC1 Initialized
VC2 Initialized
VC2 Deinitialized
VC2 Initialized
VC3 Initialized
VC3 Deinitialized
VC2 Deinitialized
The problem I am incurring is in my actual program - when I use these methods above, it automatically calls deinit on the last object I created when I CREATE my new object. But it doesn't actually delete anything - the memory graph will show x number of items even though the deinit method is called.
import UIKIT
class myClass: UIViewController {
weak var pageController:PageController?
override func viewDidLoad() {
super.viewDidLoad()
print("Initialized PC")
presentThemes()
}
func presentThemes() {
let pg = PageController(x: 1, controller: self)
pageController = pg
self.present(pageController!, animated: true, completion: nil)
}
deinit {
print("Deinit")
}
}
The output I am being provided is:
Initialized PC
On the first instance and is:
Deinit
Initialized PC
On the second instance. Any idea why Deinit is being called but the memory graph shows it there?

How to get the UIButton ID?

I have created 5 buttons dynamically in Swift like this:
for theIndex in 0..<5 {
let aButton = UIButton()
aButton.frame = CGRectMake(self.view.bounds.width / 2 - 120, self.view.bounds.height / 2 - 150, 240, 300)
aButton.setTitle("Button \(theIndex)", forState: .Normal)
aButton.addTarget(self, action: "btnClicked:", forControlEvents:.TouchUpInside)
self.view.addSubview(aButton)
}
But how can I get the BUTTON_ID for each one?
func btnClicked(sender: AnyObject?) {
let vc = storyboard!.instantiateViewControllerWithIdentifier("vc_name") as! DetailVC
vc.selectedId = **WANT_TO_PASS_BUTTON_ID_HERE**
self.presentViewController(vc, animated:true, completion:nil)
}
You can use tag property of your buttons (for example, you can set there a button's index), and retrieve it later in func btnClicked(sender: AnyObject?) method to determine a button with which index was pushed
While cycling through your indexes do:
aButton.tag = theIndex
and in btnClicked method do:
vc.selectedId = sender.tag
See for reference Apple docs on UIView

How can I change the target of the action on a button - Swift

import Foundation
import MapKit
class CustomAnnotationView : MKPinAnnotationView
{
let selectedLabel:UILabel = UILabel.init(frame:CGRectMake(0, 0, 250, 125))
let Button:UIButton = UIButton.init(frame:CGRectMake(0, 0, 100, 38))
override func setSelected(selected: Bool, animated: Bool)
{
super.setSelected(false, animated: animated)
if(selected)
{
selectedLabel.text = annotation!.title!
selectedLabel.center.x = 0.5 * self.frame.size.width
selectedLabel.center.y = -0.5 * selectedLabel.frame.height
self.addSubview(selectedLabel)
Button.backgroundColor = UIColor.yellowColor()
Button.setTitle("Press", forState: UIControlState.Normal)
//the code below here //
var storyboard = UIStoryboard(name: "Main", bundle: nil)
let Map = storyboard.instantiateViewControllerWithIdentifier("Map")
Button.addTarget(Map, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
//self.view.addSubview(button)
Button.center.x = 0.5 * self.frame.size.width + 10
Button.center.y = selectedLabel.center.y
self.addSubview(Button)
}
else
{
selectedLabel.removeFromSuperview()
}
}
//This in Map.swift
func buttonAction () {
print("button pressed")
}
}
Instead of using Button.addTarget(self...
As i'm coding in a custom class.
How do I get it to run the action?
I've set the names also for the storyboard as Main on the right panel and same with the Map.
The Map is released after go out the setSelected method. You should handle the Map by something(property, global variable, ...)
in your CustomAnnotationView class add a block:
var tapProccess:(()->())?
in your action func
func buttonAction () {
self.tapProccess?()
}
And implement it:
let customAnno = CustomAnnotationView()
customAnno.tapProccess = {()->() in
// do some thing
}
Well, one possible solution to this would be to add a tap gesture recognizer to the button which can be done in code like this:
let tap = UITapGestureRecognizer(target: self, action: Selector("actionTap:"))
tap.delegate = self
Button.addGestureRecognizer(tap)
Then you could let the view controller conform to the class of UIGestureRecognizerDelegate.
Also, create a function that will do what you want the button to do when tapped, and name it "actionTap" like this:
func actionTap(sender: UITapGestureRecognizer? = nil) {
// Implement what you want the button to do here.
}

Button to next view controller

I want to create a Button that presents another view controller when it is tapped.
I am trying to to this via selector but when I click the button I get a Thread1: signal SAGABRT error.
So I commented out the presenting and just put in a print to console-statement. I also get the same error. :(
My code is the following:
override func viewDidLoad() {
var menuView = UIView()
var newPlayButton = UIButton()
var newPlayImage = UIImage(named: "new_game_button_5cs")
var newPlayImageView = UIImageView(image: UIImage(named: "new_game_button_5cs"))
newPlayButton.frame = CGRectMake(0, 0, newPlayImageView.frame.width, newPlayImageView.frame.height)
newPlayButton.setImage(newPlayImage, forState: .Normal)
newPlayButton.addTarget(self, action:"showNewPlay:", forControlEvents: UIControlEvents.TouchUpInside)
menuView.addSubview(newPlayButton)
self.view.addSubview(menuView)
}
func showNewPlay(sender:UIButton!) {
//var vc = self.storyboard?.instantiateViewControllerWithIdentifier("newGameID") as ViewController
//self.presentViewController(vc, animated: false, completion: nil)
println("Button tapped")
}

Resources