I am trying to create a transition between two View Controllers (the second presented modally) that passes data parsed from an API. A button is created based on this data (it affects what the button says). This is within the closure of an API call, and I store the returned data into a variable within the class (i.e. self.data = returnedData)
let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(0, 100, UIScreen.mainScreen().bounds.width, 50)
button.backgroundColor = UIColor.whiteColor()
button.setTitle("\(buttonTitleInfo)", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
self.view.addSubview(button)
Then:
func buttonAction(sender:UIButton!)
{
// the data is usable here but I don't know how to pass it via this button
let viewController:UberWebviewController = UberWebviewController()
self.navigationController?.presentViewController(viewController, animated: true, completion: nil)
}
I am not using Storyboard or IB and most of the tutorials/solutions I have found use things specific to those.
How can I pass the returned data without using those?
Make a var on UberWebviewController of the type you want, for example [Any]?:
var dataFromAPI : [Any]?
Then set it after creating the controller but before presenting it:
let viewController = UberWebviewController()
viewController.dataFromAPI = whateverYourDataIs
self.navigationController?.presentViewController(viewController, animated: true, completion: nil)
Then within UberWebviewController, unwrap the optional:
func doSomething {
if let myData = dataFromAPI {
// do something with myData
} else {
// no data was obtained
}
}
Alternatively, you could make dataFromAPI non-optional and pass the data in the initializer:
class UberWebviewController : UIViewController {
var dataFromAPI : [Any]
init(data someData : [Any]) {
self.dataFromAPI = someData
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then just pass it during initialization:
let viewController = UberWebviewController(data: whateverYourDataIs)
self.navigationController?.presentViewController(viewController, animated: true, completion: nil)
Related
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'm trying to access the rightBarButton function for showing cart from an another ViewController, although I can call that function but it won't display any image i.e cart image
Below code show how am i calling this function.
override func viewDidLoad() {
super.viewDidLoad()
// Adding Right Bar Button Item
let rightBarButton = RightBarButton()
rightBarButton.addingRightButton()
}
Below code show about the rightBarButton function.
class RightBarButton {
var storyboard = UIStoryboard()
var navigationItem = UINavigationItem()
var navigationController = UINavigationController()
func addingRightButton() {
let image = UIImage(named: "cart")
let finalImage = resizeImage(image: image!, newWidth: 30)
navigationItem.rightBarButtonItem = FFBadgedBarButtonItem(image: finalImage, target: self, action: #selector(rightButtonTouched))
let button = navigationItem.rightBarButtonItem as? FFBadgedBarButtonItem
cartCount { (cartCount) in
print("Api calling done ...")
print(cartCount)
button?.badge = "\(cartCount)"
}
}
#objc func rightButtonTouched() {
// print("Event Called")
}
func cartCount(completion: #escaping (Int) -> Void) {
// print("Api calling ...")
}
}
If I add this code inside the viewDidLoad() the image will display and the click event work as well.
If you find any solution regarding this code please help!
It doesn't work because navigationItem in your RightBarButton class is not part to of any navigation contoller (as it is manually created and not explicitly attached).
Make the created FFBadgedBarButtonItem instance to be accessible to the view controller so you can add it correctly:
override func viewDidLoad() {
super.viewDidLoad()
// Adding Right Bar Button Item
let rightBarButton = RightBarButton()
rightBarButton.addingRightButton()
navigationItem.rightBarButtonItem = rightBarButton.badgeBarItem // badgeBarItem is the property exposed to the view controller
}
The edited RightBarButton class:
class RightBarButton {
var badgeBarItem: FFBadgedBarButtonItem?
func addingRightButton() {
let image = UIImage(named: "cart")
let finalImage = resizeImage(image: image!, newWidth: 30)
badgeBarItem = FFBadgedBarButtonItem(image: finalImage, target: self, action: #selector(rightButtonTouched))
cartCount { [weak self] cartCount in
print("Api calling done ...")
print(cartCount)
self?.badgeBarItem?.badge = "\(cartCount)"
}
}
#objc func rightButtonTouched() {
// print("Event Called")
}
func cartCount(completion: #escaping (Int) -> Void) {
// print("Api calling ...")
}
}
You need to retreive an instance of needed ViewController with rightBarButton, say neededViewController. And then you can call it like here:
neededViewController.viewDidLoad()
or make another method in neededViewController's class with next content:
func addRB () {
// let rightBarButton = RightBarButton(image: UIImage(named: "imageName"), landscapeImagePhone: UIImage(named: "imageName"), style: .done, target: self, action: nil)
let nc = navigationController
let navBar = nc?.navigationBar
let but = UIBarButtonItem(image: UIImage(named: "imageName"), landscapeImagePhone: UIImage(named: "imageName"), style: .done, target: self, action: nil)
// try to create RightBarButton with defined images and a text and pass it instead of `but`
navBar?.topItem?.setRightBarButton(but, animated: false)
}
and call neededViewController.addRB()
I've customized a share button and put it in UINavigationbar to be a right bar button item, I want to click this button to present a UIActivityViewController, the code can be built successfully and run, but the share button cannot be clicked, nothing happens (even errors), just like the share button is disabled.
The demo picture:
Can anyone help me to solve it? Thank you in advance.
Code:
import UIKit
import Font_Awesome_Swift
class shareButton:UIButton{
var button:UIButton = UIButton()
init(button_frame: CGRect, connected: [UIButton]?){
super.init(frame:CGRect(x: 0, y: 0, width: 20, height: 20))
self.button.frame = button_frame
self.button.setFAIcon(icon: FAType.FAShareAlt, iconSize: 22, forState: .normal)
self.button.setFATitleColor(color: .darkGray)
self.button.addTarget(self, action:#selector(self.buttonPressed), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func construct() -> UIButton {
return self.button
}
#objc func buttonPressed() {
let url = NSURL.init(string: "http://www.probe-lab.com")
let items:[Any] = [url!]
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityViewController.excludedActivityTypes = [.print,
.assignToContact,.saveToCameraRoll,.addToReadingList,.openInIBooks]
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController?.present(activityViewController, animated: true, completion: nil)
}
}
In viewcontroller I used the button like this:
override func viewDidLoad() {
super.viewDidLoad()
let btn = shareButton(button_frame: CGRect(x:0, y:0, width: 30, height:30), connected:nil ).construct()
let item = UIBarButtonItem(customView: btn)
self.navigationItem.rightBarButtonItem = item
}
First of all, please be sure to Capitalize class names. That is the convention in Swift. Now, moving on to the solution:
You don't need to subclass UIButton to do get your desired result.
Simply add the "buttonPressed" code as action for the barbuttonitem.
Trying to create a UIButton subclass that internally instantiates another UIButton and sends that button to the ViewController which then casts it into UIBarButtonItem is a very convoluted way of doing it.
You can download my solution from this repository:
https://github.com/asabzposh/StackOverFlow-48554026.git
If you desire to create a UIButton subclass, then do it this way:
import UIKit
class ShareButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
// Set title, background color, etc.
let imageView = UIImageView(frame: frame)
let image = UIImage(named: "p.jpeg")
imageView.image = image
imageView.contentMode = .scaleAspectFill
self.addSubview(imageView)
self.addTarget(self, action: #selector(internalButtonPressed), for: .touchUpInside)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#objc func internalButtonPressed() {
let url = NSURL.init(string: "http://www.probe-lab.com")
let items:[Any] = [url!]
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityViewController.excludedActivityTypes = [.print, .assignToContact,.saveToCameraRoll,.addToReadingList,.openInIBooks]
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController?.present(activityViewController, animated: true, completion: nil)
}
}
Then to use it in your ViewController:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
// 1. Solution A
// let item = UIBarButtonItem(title: "Share", style: .plain, target: self, action: #selector(buttonPressed))
// 2. Solution B
let button = ShareButton(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
let item = UIBarButtonItem()
item.customView = button
// 3. Finally Add barbutton item
self.navigationItem.rightBarButtonItem = item
}
#objc func buttonPressed() {
let url = NSURL.init(string: "http://www.probe-lab.com")
let items:[Any] = [url!]
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityViewController.excludedActivityTypes = [.print, .assignToContact,.saveToCameraRoll,.addToReadingList,.openInIBooks]
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController?.present(activityViewController, animated: true, completion: nil)
}
}
Issue is with your custom class i just tested your custom class and run small test case on that, made some changes please have a look. no need to make var button:UIButton = UIButton() in share button , just override UIbutton method.I have changed the add target function as well.
class shareButton:UIButton{
override init(frame: CGRect) {
// set myValue before super.init is called
super.init(frame: frame)
//self.button.frame = frame
isUserInteractionEnabled = true
backgroundColor = UIColor.red
addTarget(self, action: #selector(buttonPressed1( sender:)), for: UIControlEvents.touchUpInside)
// set other operations after super.init, if required
backgroundColor = .red
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func buttonPressed1(sender: UIButton) {
}
}
I got a solution and modified my code, add "var strongSelf:shareButton?" and "strongSelf = self", now it can run and present an activityViewController. I don't know if this is the best solution, but at least it works.
I found it works fine with iPhone devices and simulators but crashes with iPads, so add "activityViewController.popoverPresentationController?.sourceView = button" to solve it.
import UIKit
import Font_Awesome_Swift
class ShareButton:UIButton{
var button:UIButton = UIButton()
var strongSelf:ShareButton?
init(button_frame: CGRect, connected: [UIButton]?){
super.init(frame:CGRect(x: 0, y: 0, width: 20, height: 20))
self.button.frame = button_frame
self.button.setFAIcon(icon: FAType.FAShareAlt, iconSize: 22, forState: .normal)
self.button.setFATitleColor(color: .darkGray)
self.button.addTarget(self, action:#selector(self.buttonPressed), for: .touchUpInside)
strongSelf = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func construct() -> UIButton {
return self.button
}
#objc func buttonPressed() {
let url = NSURL.init(string: "http://www.probe-lab.com")
let items:[Any] = [url!]
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
activityViewController.excludedActivityTypes = [.print,
.assignToContact,.saveToCameraRoll,.addToReadingList,.openInIBooks]
activityViewController.popoverPresentationController?.sourceView = button
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController?.present(activityViewController, animated: true, completion: nil)
}
}
In any view I wanna use this share button just add the lines like this:
override func viewDidLoad() {
super.viewDidLoad()
let btn = ShareButton(button_frame: CGRect(x:0, y:0, width: 30, height:30), connected:nil ).construct()
let item = UIBarButtonItem(customView: btn)
self.navigationItem.rightBarButtonItem = item
}
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.
}
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)
}
}