Issue calling externally assigned closure from IBAction - ios

I have this UIView subclass:
class EmptyAlbumsView: UIView {
class func instanceFromNib() -> EmptyAlbumsView {
return R.nib.emptyAlbumsView.firstView(owner: nil)! as EmptyAlbumsView
}
// MARK: - IBActions
#IBAction func didTapFreeAlbumButton(sender: UIButton) {
print("Tapping button")
print(didTapFreeAlbumButtonClosure)
didTapFreeAlbumButtonClosure?()
}
#IBAction func didTapEventAlbumButton(sender: UIButton) {
didTapEventAlbumButtonClosure?()
}
#IBAction func didTapWhatsTheDifferenceButton(sender: UIButton) {
didTapWhatsTheDifferenceButtonClosure?()
}
#IBAction func didTapSubmitAlbumCodeButton(sender: UIButton) {
didTapSubmitAlbumCodeButtonClosure?()
}
// MARK: - Public Properties
open var didTapFreeAlbumButtonClosure: EmptyClosure? {
didSet {
log.verbose("set to \(self.didTapFreeAlbumButtonClosure!)")
}
}
open var didTapEventAlbumButtonClosure: EmptyClosure?
open var didTapWhatsTheDifferenceButtonClosure: EmptyClosure?
open var didTapSubmitAlbumCodeButtonClosure: EmptyClosure?
}
which I am initialising in a UIViewController as such:
fileprivate func showEmptyAlbumsView() {
let emptyAlbumsView = EmptyAlbumsView.instanceFromNib()
emptyAlbumsView.didTapFreeAlbumButtonClosure = {
print("hey")
}
view.addSubview(emptyAlbumsView)
emptyAlbumsView.matchEdgeAnchorsOfView(self.view)
}
I can see the Tapping button log statement from IBAction, but the second log statement over there prints nil, as it appears the didTapFreeAlbumButtonClosure has not been set, and of course not getting called ever. I never see hey printed out.
However, in the didSet block I can clearly see the closure is getting set.
EmptyClosure is defined as typealias EmptyClosure = () -> ()
What am I missing here?

Ok, after a lot of frustration, the problem was rather silly.
In the Interface Builder I had set the File Owner of the my xib to EmptyAlbumsView. That was causing the IBActions to be called properly but when I tried hooking up some IBOutlets I was getting KVC errors.
It appears the proper way to do this is leave the File Owner class empty, and set the root views of the xib to EmptyAlbumsView. Then I'd hook my IBOutlets and IBActions directly to the view.

Related

Delegate Pattern In Swift w/o Segue

I cannot find an example of the Swift Delegate pattern without the segue included. I want the boss class(boss view) to send orders to the worker class(worker view) and print what the boss says to print. The Delegate is coming up 'nil' when I press either of the buttons (doThisButton/doThatButton) on the boss page.
Edit: My Views are set up as follows: the 'TheBoss' VC has three buttons, 'doThisButton', 'doThatButton', and 'goToTheWorker' button. the 'TheWorker' VC has a text box. The 'doThisButton'/'doThatButton' send information to the text box in 'TheWorker' VC, but do not make 'TheWorker' VC appear. the 'goToTheWorker' button is a show segue that was set up in storyboard to open the 'TheWorker' VC.
This is the 'Boss' class
import UIKit
protocol TheBossesOrdersDelegate {
func doThis(numberOne: Int, stringOne: String)
func doThat(numberTwo: Int, stringTwo: String)
}
class TheBoss: UIViewController {
// Declair Delegate
var delegate: TheBossesOrdersDelegate!
#IBAction func doThisButton(_ sender: UIButton) {
delegate.doThis(numberOne: 75, stringOne: "Do This")
}
#IBAction func doThatButton(_ sender: UIButton) {
delegate.doThat(numberTwo: 125, stringTwo: "Do That")
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
This is the 'Worker' class
import UIKit
class TheWorker: UIViewController, TheBossesOrdersDelegate {
let theBoss = TheBoss()
func doThis(numberOne: Int, stringOne: String) {
print("the boss send this number to print: \(numberOne) and this string: \(stringOne)")
theWorkersTextBox.text = "Number: \(numberOne) String:\(stringOne)"
}
func doThat(numberTwo: Int, stringTwo: String) {
print("the boss send this number to print: \(numberTwo) and this string: \(stringTwo)")
theWorkersTextBox.text = "Number: \(numberTwo) String:\(stringTwo)"
}
#IBOutlet weak var theWorkersTextBox: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
theBoss.delegate = self
}
}
It looks like your code has the worker create the boss. Since you do that, you need to tell the boss who it's delegate is:
import UIKit
class TheWorker: UIViewController, TheBossesOrdersDelegate {
let theBoss = {
let result = TheBoss()
result.delegate = self //This is the line you are missing
return result
}()
//The rest of your TheWorker class's code...
}
EDIT:
Based on your comment, you have a confused mess. Post the Boss class code for the button that displays a worker view controller. And your title says "w/o segue", but then in your comment you say "the boss is presented first and contains a button that connects to the worker via a show segue." What? you said without a segue.

Delegate not executing after call swift

I have a viewController with another containerView insider set up to appear temporarily (added programmatically). The containerView is a sort of operation bar, which allows you to change values of the viewController. The protocol called from an IBAction of a button however, does not call the protocol set up inside the viewController class.
Here is the code from both classes:
class viewController: UIViewController, updateListDelegate {
let dataSource = containerView()
override func viewDidLoad() {
super.viewDidLoad()
dataSource.delegate = self
}
func updateList(sender: containerView) {
print("is called") //is not printed
}
}
The code from the containerView:
protocol updateListDelegate {
func updateList(containerView)
}
class containerView: UIViewController {
var delegate: updateListDelegate?
#IBAction func AddSong(_ sender: UIButton) {
self.delegate?.updateList(sender: self)
}
}
If this method is only to be called from one object, then, in my opinion, I would not define a protocol. If multiple objects are to call this method, then I would define a protocol. This is typically how you would call a method backwards, using a basic delegate.
class ViewController: UIViewController {
let container = ContainerView()
override func viewDidLoad() {
super.viewDidLoad()
container.viewControllerDelegate = self
// push to this instance of container at some point
}
func doSomething() {
print("great success")
}
}
class ContainerView: UIViewController {
weak var viewControllerDelegate: ViewController?
#objc func someAction() {
if let viewControllerDelegate = viewControllerDelegate {
viewControllerDelegate.doSomething()
}
}
}
// prints "great success" when someAction() called
One of the most common mistakes people make is not keeping track of instances. For delegates to work, you must be sure you are using the specific instances that you've instantiated and assigned those delegates to.

Swift - Invoke Custom Delegate Method

I am using BMPlayer library and want to implement custom control, for which I have the following class which confirm to following protocol
#objc public protocol BMPlayerControlViewDelegate: class {
func controlView(controlView: BMPlayerControlView, didChooseDefition index: Int)
func controlView(controlView: BMPlayerControlView, didPressButton button: UIButton)
func controlView(controlView: BMPlayerControlView, slider: UISlider, onSliderEvent event: UIControlEvents)
#objc optional func controlView(controlView: BMPlayerControlView, didChangeVideoPlaybackRate rate: Float)
}
open class BMPlayerControlView: UIView {
open weak var delegate: BMPlayerControlViewDelegate?
open weak var player: BMPlayer?
// Removed rest of the code for clarity
open func onButtonPressed(_ button: UIButton) {
autoFadeOutControlViewWithAnimation()
if let type = ButtonType(rawValue: button.tag) {
switch type {
case .play, .replay:
if playerLastState == .playedToTheEnd {
hidePlayToTheEndView()
}
default:
break
}
}
delegate?.controlView(controlView: self, didPressButton: button)
}
}
I am extending BMPlayerControlView class to extend the control view using the following code.
class BMPlayerCustomControlStyle3: BMPlayerControlView {
}
class BMPlayerStyle3: BMPlayer {
class override func storyBoardCustomControl() -> BMPlayerControlView? {
return BMPlayerCustomControlStyle3()
}
}
My question is, how do I invoke didPressButton delegate method? I don't want to overwrite onButtonPressed, I tried the following
extension BMPlayerCustomControlStyle3:BMPlayerControlViewDelegate {
func controlView(controlView: BMPlayerControlView, didChooseDefition index: Int) {
}
func controlView(controlView: BMPlayerControlView, didPressButton button: UIButton) {
print("Did Press Button Invoked")
}
func controlView(controlView: BMPlayerControlView, slider: UISlider, onSliderEvent event: UIControlEvents) {
}
}
And this doesn't seem to work, what am I missing here?
Thanks.
If you want your BMPlayerControlView subclass to also act as the delegate object, you need to set the delegate property as well (and conform to the BMPlayerControlViewDelegate protocol as you are already doing).
One way to do so is by overriding the delegate superclass property in your subclass:
class BMPlayerCustomControlStyle3: BMPlayerControlView {
override open weak var delegate: BMPlayerControlViewDelegate? {
get { return self }
set { /* fatalError("Delegate for internal use only!") */ }
}
}
Of course, when using the delegate internally such as this, you won't allow it to be used by BMPlayerControlView clients at all. The overridden set above ensures you get an error if trying to do so.

how can I call a method stored in a parent uiviewcontroller from the uiviewcontroller embedded in a container in that controller?

I have a UIViewController -let's call it parentViewController - and it contains a container. This container has embeddedViewController embedded in it.
Now, my parentViewController contains a method that prints something into the console:
func printSomeData(){
print("some data")
}
embeddedViewController has a button with action asigned to it:
#IBAction func printSomething(sender: AnyObject) {
is there a way that I could call method printSomeData from printSomething in Swift?
There are couple of ways such as by implementing delegate or by posting NSNotification. Here I show the sample delegation pattern. This is exact scenario as your own controller but from this you can get some concept and if you implement this hope this will accomplished your goal.
class ParentController:UIViewController,printing {
override func viewDidLoad() {
//
}
func presentEmbadedController(){
let embadedVC = EmbadedController()
embadedVC.delegate = self
}
func printSomeData() {
print("some date")
}
}
Here is the protocol something like this
protocol printing{
func printSomeData()
}
And then the EmbadedController like this
class EmbadedController:UIViewController {
var delegate: printing?
override func viewDidLoad() {
//
}
#IBAction func printSomething(sender: AnyObject) {
if let _ = delegate{
delegate?.printSomeData()
}
}
}
You can use NSNotificationCenter.defaultCenter().addObserver(...)

UIButton inside a custom input view doesn't invoke any methods

I created a custom input view with a couple of UIButtons inside it. All these buttons invoke one single IBAction.
The IBAction is not called although I'am 100% sure I set them properly in IB. To prove that this isn't causing the problem I added a target to one of the buttons programmatically, but the action still won't get invoked.
import UIKit
protocol CashDeskInputDelegate: class {
func didReceiveInput(inputValue: String)
func didReceiveDoneMessage()
}
class CashDeskInputViewController: UIInputViewController {
// Delegate
weak var delegate: CashDeskInputDelegate?
// Actions
#IBAction func buttonTapped(sender: UIButton) {
print("buttonTapped")
guard let valueOfSender = sender.titleLabel?.text else {
dismissKeyboard()
return
}
valueOfSender == "Done" ? delegate?.didReceiveDoneMessage() : delegate?.didReceiveInput(valueOfSender)
}
// Test outlet
#IBOutlet weak var firstButton: UIButton!
override func viewDidLoad() {
// Add an action to the first button for testing purposes.
firstButton.addTarget(self, action: "buttonTapped:", forControlEvents: .TouchUpInside)
}
}
I didn't implement the delegate properly yet, but that not the the point. The print("buttonTapped") won't even show up. The method just doesn't get called, I tested this using breakpoints.
Does anyone know what is causing this problem

Resources