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")
}
Related
I have an app that has multiple views and one of them is a SceneView as you can see below. The SceneView is not the initial view. Users can open the SceneView by triggering a segue from another ViewController but when I want to add a nav bar to the SceneView, an error has occured.
So how can I add a navigation bar or a button to a sceneView? If there is no way, how can I manage to dismiss segue from the SceneView?
I couldn't find a way to add a navigationBar on top of a sceneView but I figured out how to add a button to a sceneView. If we create a button programmatically and add that button to the sceneView, we can navigate via that button.
Here's what we can do;
import UIKit
import SceneKit
class ViewController: UIViewController {
var scnView: SCNView?
var exampleScn = SCNScene(named: "ExampleScn")
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.scnView = SCNView(frame: self.view.frame)
self.scnView?.scene = exampleScn
self.view.addSubview(self.scnView!)
let button = UIButton(type: .system)
button.tintColor = UIColor.black
button.titleLabel?.font = UIFont(name: "Arial", size: 25)
button.setTitle("Back", for: .normal)
button.sizeToFit()
button.addTarget(self, action: #selector(didPressBack), for: .touchUpInside)
button.center.x = 50
button.frame.origin.y = 20
scnView.addSubview(button)
}
#objc func didPressBack (sender: UIButton!) {
dismiss(animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
if we do this, the result will look like this:
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!
I am trying to pop a view off programmatically but am having trouble.
import UIKit
class CreatePostViewController: UIViewController {
var isAnimating: Bool = false
var dropDownViewIsDisplayed: Bool = false
override func viewDidLoad() {
super.viewDidLoad()
createDescriptionField()
createLabels()
createInputFields()
createBackButton()
}
//More code omitted
func createBackButton(){
let button = UIButton()
button.setTitle("back", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.frame = CGRectMake(200,200,100,50)
button.backgroundColor = UIColor.redColor()
button.addTarget(self, action: "goBack:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func goBack(sender: UIButton){
navigationController?.popToRootViewControllerAnimated(true)
}
}
I add this class from a another class like this:
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
needCashButton()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func needCashButton(){
let image = UIImage(named: "GlossyRoundedButton.png") as UIImage?
let button = UIButton()
button.setTitle("Do Job", forState: .Normal)
button.setTitleColor(UIColor.blueColor(), forState: .Normal)
button.setBackgroundImage(image, forState: .Normal)
button.frame = CGRectMake(225,200,100,50)
button.addTarget(self, action: "cashButtonPressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(button)
}
func cashButtonPressed(sender: UIButton){
let findJob = CreatePostViewController()
self.presentViewController(findJob, animated: false, completion: nil)
}
}
I read many other posts that suggested I just pop the CreatePostViewController with the line: navigationController?.popToRootViewControllerAnimated(true) but that doesn't do anything when I click the button. Is there something I am overlooking here?
If your UIViewController is NOT in a UINavigationViewController (or it was presented modally), use this to dismiss it:
self.dismissViewControllerAnimated(true, completion: nil)
If you have it inside a UINavigationViewController, dismiss it using this:
self.navigationController.popViewControllerAnimated(true)
navigationController works only if you actually use Navigation controller :). So I would suggest to use something like this
let vc = UINavigationController(rootViewController: CreatePostViewController())
self.presentViewController(vc, animated: false, completion: nil)
I have a class like this,
import UIkit
class One {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Go", forState: UIControlState.Normal)
btn.addTarget(self, action: "goToClassTwo", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func goToClassTwo(){
if(AppGlobals().getIsFromDiffView()){
let difView = UINavigationController(rootViewController: DiffView())
difView.pushViewController(Two(), animated: true)
}else{
self.navigationController?.pushViewController(Two(), animated: true)
}
}
}
A setter/getter class like this,
class AppGlobals: NSObject {
var isFromDiffView = false
func setIsFromDiffView(val: Bool){
isFromDiffView = val
}
func getIsFromDiffView() -> Bool {
return isFromDiffView
}
}
And I have another class like this,
class DiffView {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Push", forState: UIControlState.Normal)
btn.addTarget(self, action: "btnAction", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func btnAction(){
AppGlobals().setIsFromDiffView(true)
One().goToClassTwo()
}
}
I am facing a problem here. When the 'Go' button in the class 'One' is tapped, then the 'Two' view controller is shown. But when I tap on the 'Push' button in the class 'DiffView' is tapped, the 'Two' view controller is not being shown.
I have checked setting breakpoints. The control does come to the goToClassTwo function in the class 'One' and the if path is being executed. But the 'Two' view controller is not shown. difView.pushViewController is called. But it is not pushing to the next view.
NOTE: I am not using storyboard
Any help would be appreciated!
This is the updated code.
Code for class 'One':
import UIKit
class One {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Go", forState: UIControlState.Normal)
btn.addTarget(self, action: "goToClassTwo", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func goToClassTwo(){
if(AppGlobals().getIsFromDiffView()){
//Using the navigation controller of DiffView
AppGlobals().getController().pushViewController(Two(), animated: true)
}else{
self.navigationController?.pushViewController(Two(), animated: true)
}
}
}
setter/getter class:
class AppGlobals: NSObject {
var isFromDiffView = false
var cntrlr: UINavigationController!
func setIsFromDiffView(val: Bool){
isFromDiffView = val
}
func getIsFromDiffView() -> Bool {
return isFromDiffView
}
//Setting and getting DiffView Navigation controller
func setController(cntrl: UINavigationController){
cntrlr = cntrl
}
func getController() -> UINavigationController {
return cntrlr
}
}
DiffView class:
class DiffView {
let btn = UIButton()
override func viewDidLoad(){
super.viewDidLoad()
btn.frame = CGRectMake(10, 20, 30, 30)
btn.setTitle("Push", forState: UIControlState.Normal)
btn.addTarget(self, action: "btnAction", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(btn)
}
func btnAction(){
AppGlobals().setIsFromDiffView(true)
//Setting the navigation controller
AppGlobals().setController(self.navigationController!)
One().goToClassTwo()
}
}
With this updated code, class 'Two' view controller is being displayed.
Thank you #zp_x for your help.
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)
}
}