I have a problem adding a target from a view controller to the button that is created in a view (no action when it is pressed). But if I create the button in the view controller directly then it works.
What could be wrong here?
class ViewController: UIViewController {
var mainView = MainView()
var counter:Int = 0
#objc func buttonAction(sender:UIButton!)
{
counter += 1
print("Button was predded:\(counter)")
mainView.lable1.text = "Button was predded:\(counter)"
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mainView)
mainView.buttno1.addTarget(self, action: #selector(buttonAction), for: UIControl.Event.touchUpInside)
}
}
class MainView : UIView {
var background: UIView = {
return background
}()
lazy var buttno1: UIButton = {
var btn = UIButton()
....
return btn
}()
func createButton1(){
print("Main view btn created")
background.addSubview(buttno1)
}
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(background)
createButton1()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Related
I want to call an action from a button which is inside a custom View. The view itself is part of a UIViewController. But when I tapped the view, nothing happens. I do not know where my mistake is, although my code looks like the ones on stackoverflow.
protocol StoreDelegate: class {
func didPressButton(_ sender: UIButton)
}
class Store: UIView {
weak var delegate:StoreDelegate?
var button: UIButton = {
let button = UIButton()
button.setTitle("button", for: .normal)
button.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
button.backgroundColor = .red
button.addTarget(self, action: #selector(buttonPress(_:)), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame:frame)
self.addSubview(button)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#objc func buttonPress(_ sender: UIButton) {
delegate?.didPressButton(sender)
print("here")
}
}
And this is my ViewController:
class ViewController: UIViewController, StoreDelegate{
var testView = Store()
override func viewDidLoad() {
super.viewDidLoad()
testView.delegate = self
self.view.addSubview(testView)
}
func didPressButton(_ sender: UIButton) {
print("Hello")
}
}
Neither it prints "Hello" nor "Here". Maybe I have misunderstood the protocol/delegate pattern.
You need to set constraints or a frame to the view
testView.frame = ///////
It's the base behind receiving actions
You can implement override sizeToFit in Store class to match your needs:
override func sizeToFit() {
frame.size = button.frame.size
}
Then you can call it like:
testView.sizeToFit()
You may consider using autolayout for better sizing.
I have an app where there is a "DadViewController" which contains a UIScrollView that has paging enabled. Each page is populated with a different UIViewController.
How Do I push a new UIViewController, from a button tap, within one of the controllers contained in the UIScrollView?
class DadView: UIView {
let model = DadModel()
let scrollView: UIScrollView = {
let view = UIScrollView()
view.isPagingEnabled = true
// Additional setup...
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupScrollView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate func setupScrollView() {
self.addSubview(scrollView)
// Autolayout code to pin scrollview to all 4 sides
let vc1 = VC1()
scrollView.addSubview(vc1.view)
vc1.view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
// Add additional view controller views with the appropriate frames...
}
}
class DadController: UIViewController {
var dadView: DadView!
override func loadView() {
super.loadView()
dadView = DadView()
view = dadView
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
}
}
Change your DadView setupScrollView to accept a UIViewController instance as argument. And don't call this method in init
class DadView: UIView {
let model = DadModel()
let scrollView: UIScrollView = {
let view = UIScrollView()
view.isPagingEnabled = true
// Additional setup...
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
// setupScrollView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate func setupScrollView(_ parentVC: UIViewController) {
self.addSubview(scrollView)
// Autolayout code to pin scrollview to all 4 sides
let vc1 = VC1()
vc1.view.frame = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT)
parentVC.addChild(vc1)
scrollView.addSubview(vc1.view)
vc1.didMove(toParent: parentVC)
// Add additional view controller views with the appropriate frames...
}
}
In DadViewController after creating DadView instance call setupScrollView method with self
class DadController: UIViewController {
var dadView: DadView!
override func loadView() {
super.loadView()
dadView = DadView()
dadView.setupScrollView(self)
view = dadView
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Then you can get parent view controller from child viewcontroller and perform push
class VC1: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
}
#IBAction func buttonAction(_ sender: UIButton) {
self.parent?.navigationController?.pushViewController(NewVC(), animated: true)
}
}
I have controllingView, that needs to change a property in presentingView. They are both in the same ViewController.
I can let them communicate by making presentingView delegate of controllingView. But it would be far more elegant and flexible, if I could just change the property directly (since
I need to change the presentingView's property's property actually)
I have seen it done in this question: Accessing protocol property in Swift class.
But in controllingView, calling delegate.propertyINeedToChange is nil.
How do I change a delegate's property from the delegating object?
Here is the code:
class MainViewController : UIViewController {
var controllingView = ControllingView()
let presentingView = PresentingView()
override func loadView() {
let view = UIView()
view.backgroundColor = .white
view.addSubview(controllingView)
view.addSubview(presentingView)
self.view = view
controllingView.delegate = presentingView
}
}
class ControllingView: UIView {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
//ControlsView Setup
self.backgroundColor = UIColor(displayP3Red: 0.9, green: 0.9, blue: 0.9, alpha: 1.0)
setupViews()
}
let testSLDR = UISlider()
var delegate: ControlsViewDelegate?
func setupViews() {
self.addSubview(testSLDR)
testSLDR.addTarget(self, action: #selector(testSLDRchanged), for: .valueChanged)
}
#objc func testSLDRchanged() {
delegate?.button?.backgroundColor = UIColor.red
}
}
class PresentingView: UIView, ControlsViewDelegate {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
let button = Button()
self.addSubview(button)
}
var button: Button?
}
protocol ControlsViewDelegate {
var button: Button? { get set }
}
class Button: UIButton { ... }
As the views are initialized in the same view controller you don't need protocol / delegate
Delete the protocol and the associated code
In ControllingView declare a weak Button property
weak var presentingButton : Button?
Replace the line to set the delegate with a line to assign the button of PresentingView to the presentingButton property
controllingView.delegate = presentingView
controllingView.presentingButton = presentingView.button
In the action change the color
#objc func testSLDRchanged() {
presentingButton?.backgroundColor = UIColor.red
}
I created a customTableViewFooter programmatically like this:
import UIKit
class CustomFooterView: UIView {
let myLabel: UILabel = {
let label = UILabel()
label.font = UIFont.systemFont(ofSize: 14, weight: UIFontWeightMedium)
label.textColor = UIColor.black
label.numberOfLines = 1
label.text = "Blah blah blah"
label.textAlignment = .center
return label
}()
let mySwitch: UISwitch = {
let mySwitch = UISwitch()
mySwitch.addTarget(self, action: #selector(switchChanged), for: .valueChanged)
return mySwitch
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
self.layout()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
self.setup()
self.layout()
}
private func setup() {
self.addSubview(myLabel)
self.addSubview(mySwitch)
}
private func layout() {
anonymousLabel.constrainCenterVertically()
anonymousLabel.constrainCenterHorizontally()
anonSwitch.constrainCenterVertically()
anonSwitch.constrainTrailing(at: 20)
}
func switchChanged() {
print("hello")
}
}
Inside my class which implements the UITableView, I add this footer programmatically:
class MyViewController: UIViewController {
var footerView = CustomFooterView()
override func viewDidLoad() {
super.viewDidLoad()
addFooter()
}
func addFooter() {
tableView.tableFooterView = footerView
}
...
}
At the moment, I have the method: switchChanged inside the class where I declare my UISwitch, and what I would like to do, is move this method to my ViewController, so the that the UISwitch, when selected by the user, calls a method inside my ViewController. How would I do this?
This should work (as already suggested in a comment)
footerView.mySwitch.addTarget(self, action: #selector(aSelector), for: .valueChanged)
I have a custom uiview (call it navBarMenu) with five buttons as subviews. I added this navBarMenu to the navigationbar's titleView. The navBarMenu has its own class and nib file where the buttons are defined as iboutlets. How can I add targets so that when I click the button in the navBarMenu it will trigger a function in my viewcontroller that is embedded in the navigation controller?
Using the method: navBarMenu.Button1.addTarget(self, action: "Button1Tapped:", forControlEvents: UIControlEvents.TouchUpInside) does not work and I get an error that it unexpectedly found nil while unwrapping an Optional value.
Edit: Here is the code for my view controller and my custom view.
class MyCustomView: UIView {
var view: UIView!
var nibName: String = "MyCustomView"
#IBOutlet weak var Button1: UIButton!
#IBOutlet weak var Button2: UIButton!
#IBOutlet weak var Button3: UIButton!
#IBOutlet weak var Button4: UIButton!
#IBOutlet weak var Button5: UIButton!
var Button1Text: String? {
get {
return Button1.titleLabel?.text
}
set(Button1Text) {
Button1.setTitle(Button1Text, forState: UIControlState.Normal)
}
}
var Button2Text: String? {
get {
return Button2.titleLabel?.text
}
set(Button2Text) {
Button2.setTitle(Button2Text, forState: UIControlState.Normal)
}
}
var Button3Text: String? {
get {
return Button3.titleLabel?.text
}
set(Button3Text) {
Button3.setTitle(Button3Text, forState: UIControlState.Normal)
}
}
var Button4Text: String? {
get {
return Button4.titleLabel?.text
}
set(Button4Text) {
Button4.setTitle(ButtonText4, forState: UIControlState.Normal)
}
}
var Button5Text: String? {
get {
return Button5.titleLabel?.text
}
set(Button5Text) {
Button5.setTitle(ButtonText5, forState: UIControlState.Normal)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
println("view is setup")
}
convenience init(controller: UIViewController) {
var frameForNavBar: CGRect = CGRect(x: 0.0, y: 0.0, width: 548.0, height: 33.0)
self.init(frame: frameForNavBar)
Button1.addTarget(controller, action: "Button1Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
Button2.addTarget(controller, action: "Button2Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
Button3.addTarget(controller, action: "Button3Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
Button4.addTarget(controller, action: "Button4Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
Button5.addTarget(controller, action: "Button5Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
addSubview(view)
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
class MyViewController: UIViewController, UINavigationBarDelegate {
#IBOutlet weak var webView: UIWebView!
func Button1Tapped(sender: AnyObject) {
println(__FUNCTION__)
}
func Button2Tapped(sender: AnyObject) {
println(__FUNCTION__)
}
func Button3Tapped(sender: AnyObject) {
println(__FUNCTION__)
}
func Button4Tapped(sender: AnyObject) {
println(__FUNCTION__)
}
func Button5Tapped(sender: AnyObject) {
println(__FUNCTION__)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: "MyViewController", bundle: nil)
}
convenience init() {
self.init(nibName: "MyViewController", bundle: nil)
tabBarItem.title = "MyVC"
tabBarItem.image = UIImage(named: "MyImage")
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let navBarMenu = MyCustomView(controller: self)
positionForBar(navigationController!.navigationBar)
self.navigationItem.titleView = navBarMenu
self.navigationController?.navigationBar.translucent = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadWebPage() {
}
func positionForBar(bar: UIBarPositioning) -> UIBarPosition {
return UIBarPosition.TopAttached
}
}
When you initialize your customView you can pass the UIViewController parameter and then add target for your button to it.
let myCustomView = MyCustomViewClass(self)
And in your MyCustomViewClass you can use:
init (controller: UIViewController) {
// use controller, not self
button.addTarget(controller, action: "Button1Tapped:", forControlEvents: UIControlEvents.TouchUpInside)
}