I wrote a custom class called PressableView. It recognizes taps and then calls a protocol function on its delegate object. Since it is a subclass of UIView, it does not allow the connection of IBActions by default.
I was wondering whether it is possible to connect a function inside the view controller, the pressable view is in, to the object, so it calls that method on tap – just like you would with a UIButton.
I already tried things like:
class PressableView: UIView {
// ...
#IBOutlet var action: (() -> Void)?
// ...
}
...but, Xcode doesn't allow that type to be an IBOutlet.
Any ideas?
change PressableView parent class as UIControl class then you can connect for actions and handle it.
UIControl is subclass of UIView class only. so you will have all the properties of UIView as well.
class PressableView: UIControl {
#IBAction func uicontrolEventAction(_ sender: Any) {
}
}
Related
I have a custom UIControl that has three subviews. Each of those subviews, I add a target:
button.addTarget(self, action: #selector(buttonTapped(clickedBtn:)), for: .touchUpInside)
Within that function buttonTapped, it does some special animations to do some transitions (It mimics the segmented control).
Now, within the ViewController that this custom UIControl exists in must know when it's touched. I created an #IBAction function that interacts with the touch events for the custom UIControl.
The problem is, that isn't possible (as far as I know). If I add a target touch event to the subviews, the parent touch events won't get called. To have the parent view called the #IBAction function, I must set all the subview's setUserInteractiveEnabledtotrue`. When I do that, the subview's touch event functions won't get called.
I need both touch event functions to be called. How can I do this? Or what's the best way to get around this?
Use delegates, add a protocol in your UIControl that needs to be implemented in your ViewController.
This way you can detect if a button is clicked in your UIControl and invoke a specific function in your VC.
For Example:
//YourUIControl.Swift
protocol YourUIControlDelegate {
func didTapFirstButton()
}
class YourUiControl : UIView { //I'm assuming you create your UIControl from UIView
var delegate : YourUIControlDelegate?
//other codes here
.
.
.
#IBAction func tapFirstButton(_ sender: AnyObject) {
if let d = self.delegate {
d.didTapFirstButton()
}
}
}
//YourViewController.Swift
extension YourViewController : UIControlDelegate {
func didTapFirstButton() {
//handle first button tap here
}
}
For simplicity, let's say I want to create a custom UITextField and I want to add a simple behaviour to it; Which is, if the textfield becomes the first responder, the background color would be changed to green.
To do so, in my custom class I have to set the class as the delegate to receive the event of becoming first responder. But the thing is that if the user of this custom textfield set itself as the delegate the events are not sent to the custom textfield(Since only one object can be the delegate of another object)
I can manually forward all the events, but I'm looking for a cleaner and more scalable solution.
Here's a sketch of the situation:
class MyTextField: UITextField {
override func awakeFromNib() {
super.awakeFromNib()
delegate = self
}
}
extension MyTextField: UITextFieldDelegate {
func textFieldDidBeginEditing(textField: UITextField) {
backgroundColor = UIColor.greenColor()
}
}
but if the user of MyTextField do this:
class ViewController: UIViewController {
#IBOutlet weak var myTextField: MyTextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.delegate = self
}
}
the behaviour won't work; because the delegation relationship to MyTextField is gone.
NOTE: I'm not only interested in becoming first responder problem, rather it's about using any methods of the delegate, with capability of the user of my custom UITextField setting itself as the delegate, at the same time.
Thanks, in advance.
As you say, most delegation is restricted to a single object as the delegate.
Since a text field is a responder, you should be able to override func becomeFirstResponder() -> Bool to change the color, while letting the user of the object handle the delegation as it expects.
UIResponder docs: "Subclasses can override this method to update state or perform some action such as highlighting the selection."
I have an UIViewController
class WelcomeViewController: UIViewController
and an UIView
class SignUpView: UIView
Now I want to set in my WelcomeViewController delegate of SignUpView:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class SignUpView: UIView { ... }
and connect it in
class WelcomeViewController: UIViewController, SegueDelegate {
how can I set in my WelcomeViiewController those delegate? When I'm trying to set:
override func viewDidLoad() {
SignUpView.delegate = self
}
it returns me
Instance member 'delegate' cannot be used on type 'SignUpView'
how can I find a solution?
You are trying to set delegate to a class. It should be an instance of the class i.e
let signUpView = SignUpView()
signUpView.delegate = self
What would be the point in doing that? If you want to navigate from one View to another, just add that Segue in Storyboard with an Identifier, so you can call self.performSegueWithIdentifier("IdentifierOfSegue", sender: self)
Create a weak property in SignUpView of that delegate(protocol) and name it other than delegate
then you can set and use it.
I agree with the developers saying "you can just do that via segue" but
the problem is you didn't declare a delegate var in the SignUpView class
so you can implement it in the signIn , if you declared it please write the line of code for me in a comment to check it
for now ...
I can suggest that you make a subview to be a parent class then override
which method you want to call
and you need to declare the delegate var as an optional (so you won't have
a memory cycle) like the following line ...
var delegate: SegueDelegate?
Let's solve this for people in need whom could need a solution when reading this issue:
In your UIView:
class SignUpView: UIView
you need to add:
var delegate : SegueDelegate?
Now, still in your class SignUpView, you need to add the function you want to delegate, just like this:
func runSegue(identifier: String) {
delegate?.runSegue(identifier)
}
This will call your delegate:
protocol SegueDelegate {
func runSegue(identifier: String)
}
Now, in your ViewController, you should have your SignUpView somewhere (created programmatically or linked through Storyboard / XIB).
In your viewDidLoadfunction, add: signUpView.delegate = self.
Don't forget to add SegueDelegatein your class heritage.
I have customView and and there is UITextField, and in my ViewController, I am using a lot of this views. So in customView.swift I have UITextFieldDelegate. But I want to do some actions on this callback in my ViewController. Example :
when I tap on textfield, delegate executes
func textFieldDidBeginEditing(textField: UITextField) {
println("delegate")
}
and after this I want some function that works with ViewController UI to execute, example:
func textFieldDidBeginEditing(textField: UITextField) {
println("delegate")
ViewController().check()
}
But it gets nil, so I want to create some callback, can you advise right way to do this?
Create a custom property of type ViewController in your customView class. In the viewDidLoad function of ViewController, call
theCustomView.viewController = self
(where theCustomView is the instance - you probably have a reference to this as well, otherwise create an IBOutlet)
Then, in the textFieldDidBeginEditing function you can just use
self.viewController.check()
I'm fairly new to iOS development and I ran into a problem which seems simple yet I cannot solve it whichever way I try.
I have a custom view class #IBDesignable class ValidatedInputFieldView: UIView that hold 2 UI elements.
One of those is UITextField.
ValidatedInputFieldView is added to its parent view class ViewController: UIViewController, UITextFieldDelegate.
I want ViewController to respond to textFieldShouldReturn event from UITextField, and not the ValidatedInputFieldView where the UITextField is in.
I've tried exposing the delegate field of the UITextView:
#IBOutlet var textFieldDelegate:UITextFieldDelegate?
And setting it in the ValidatedInputFieldView:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
contentTextField.delegate = textFieldDelegate
}
And then linking it in the IB using the workaround:
Declare the outlet's type as AnyObject or NSObject, connect objects to the outlet using Interface Builder, then change the outlet's type back to the protocol.
But it simply does not work.
Debug says the object is nil.
I'm having trouble understanding how are those events that happen in Subview passed to the Parent view and what should I use to expose delegates.
A typical style I like to follow is to keep the items contained in their Views. What I would do is put the textFieldShouldReturn function in the ValidateInputView and set the delegate of the UITextField to the ValidateInputView. Then, in the textFieldShouldReturn function, post a notification using
NSNotificationCenter.defaultCenter().postNotificationName("textFieldFunction", object: self)
and make sure you are listening in the ViewController for this with:
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: Selector("textFieldFunction:"),
name: "textFieldFunction",
object: nil)
and create the function textFieldFunction in the ViewController to handle it like this:
func textFieldFunction(notification: NSNotification) {
//Put code to handle return press here
}
Make sure you put this in the ViewController as well:
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
OR for no notification
Use the solution in this sample project:
https://mega.nz/#!koYDTZpa!uGZ6oUbKxRaWuGSM9FbCuV0t8oz5mtu35rZlLEL7Ehs
Sorry, I would post code, but there isn't much since it is mostly all through the IB.