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.
Related
I have a custom init? method on destination scene called ListCountriesViewController which is called inside a method "createListCountriesViewController" located in ViewController and set on a storyboard segue instantiation property at "Connection Inspector" which connects ViewController and ListCountriesViewController scenes. ListCountriesViewController contains a custom object which is created via storyboard "Object". When segue executes the method "createListCountriesViewController" is executed twice, and the second execution terminates with NSExeption:
Thread 1: "Custom instantiated
<pocStoryboardDependencyInjection.ListCountriesViewController:
0x148005e40> must be kind of class
pocStoryboardDependencyInjection.CustomObject"
after deleting the custom object from storyboard scene the problem is solved.
Is Storyboard Dependency Injection, which was included in UIKit/iOS 13, compatible with objects which are set on scene like: Objects (custom class), Additional views (custom class), Gestures (custom class) If yes, how can I solve my problem?
class ViewController: UIViewController {
#IBSegueAction
func createListCountriesViewController(_ coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ListCountriesViewController? {
let controller = ListCountriesViewController(coder, userForm: ["field1"])
return controller
}
}
class ListCountriesViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet var customObject: CustomObject!
init?(_ coder: NSCoder, userForm: UserForm) {
self.userForm = userForm
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError()
}
}
class CustomObject: NSObject {
}
Is Storyboard Dependency Injection, which was included in UIKit/iOS 13, compatible with objects which are set on scene like: Objects, Additional views, Gestures ?
Evidently not. As your example demonstrates (perfectly), the mere presence of the additional top-level object in the scene causes the segue action to misbehave. No reference / outlet to the storyboard CustomObject is needed to elicit the crash.
I regard this as a bug; you should file a report with Apple.
I am getting this error on the last brace of a init in a class of mine. The class looks something like the following (I market the spot where error happens):
class RecordingViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {
let cameraButton:UIButton?
let camPreview:UIView?
init (cameraButton: UIButton!, camPreview: UIView!) {
self.cameraButton = cameraButton
self.camPreview = camPreview
} //get error here
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//do a bunch of other stuff
}
I have looked here and here for a solution but both seem like solutions that are either really bad or that are too specific to that question, thus they have not work for me.
I was hoping for a solution to my problem done in such a way that it can help me understand why this error is happening.
Since you inherit from UIViewController, you should call super.init right after you set the variables in your init function
When you inherit a class and implement a new init function or override its own init function you should (almost) always call super.init. Let's take your example, you inherited from UIViewController. UIViewController has a few init functions that you can use to initialize a view controller. if you don't call super.init, all the code inside those functions will not get called and possibly the view controller won't get initialized.
Anyway, this piece of code should work for you:
class ViewController: UIViewController {
var button: UIButton?
init(button: UIButton) {
self.button = button
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is what I found on Swift Programming Language:
In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.
Hope this can explain that question.
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) {
}
}
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."
Here is my view class
class V_TakePhoto:UIView{
var _takePhotoCallback:(iImage)->Void?
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
_takePhotoCallback = nil
}
#IBAction func takePhoto(sender: AnyObject) {
println("Here we go!")
}
func initWithCameraCallback((iImage)->Void)
{
}
}
This class is a UIView subclass. In the interface builder I selected the ViewController class, and then selected its View object. I assigned V_TakePhoto to this view object.
In the ViewController class, which I assigned to C_TakePhoto class, I want to init the V_TakePhoto class.
As you can see, I want it to have a callback variable that it gets passed at run time. However, because the view is already getting initialized from the interface builder, init(coder) is getting called first.
As it stands right now it seems hacky that I need to have 2 init functions. One where interface builder calls it, then again when my ViewController inits the view with its callback. Also I will have a number of variables, and I need to pre-init them in the init(coder) call then RE-init them again when the ViewController calls the 'true' init on the V_PhotoClass. Seems very hacky to me, there must be a clean 'correct' way to do this.
Can you suggest a cleaner way to handle a situation where you have variables and need to init a view despite there being an init(coder) call from the interface builder?
I would suggest creating a function in V_TakePhoto and call it in both V_TakePhoto's init(coder) and ViewController's viewDidLoad(), something like :
In V_TakePhoto :
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
specialInit()
}
func specialInit() {
// some of your view initialization
}
In your View Controller :
#IBOutlet weak var takePhotoView: V_TakePhoto!
override func viewDidLoad() {
super.viewDidLoad()
// this method is called after the view controller has loaded its view hierarchy into memory.
takePhotoView.specialInit() // RE-init
}