Add custom functionality to UITextField, UILabel, UIView custom classes - ios

In my app I have some interface elements such as :CustomLabel, CustomView, CustomTextField (all are custom classes inherit from their base class: UIView, UILabel, UITextField.
At this moment I'm calling from each element DrawRect callback to a function that draws underline on itself and basically I have chunks of duplicated code on each subclass.
How can I make this three subclasses have the same DrawRect content without creating an extension of UIView because I don't want each View or subclass of UIView to have this behaviour.

For this you could use a protocol and a protocol extension to add your common methods to your subclasses. Then you'd only have to make your classes conform to that protocol and override drawRect to call the methods from your protocol. Adding the new behaviour by just conforming to the protocol and not doing any other changes is not possible, unfortunately. You can't override methods in protocol extensions, and you can't add methods that use the Objective-C runtime there either which would allow method swizzling.
In code this would look something like this:
protocol CustomDrawing {}
extension CustomDrawing where Self: UIView {
func myDrawingCode() {
// Whatever
}
}
You use it like this then:
class CustomLabel: UILabel, CustomDrawing {
override func draw(_ rect: CGRect) {
super.draw(rect)
myDrawingCode()
}
}
If you need to access some common property or method in myDrawingCode() you will have to declare them inside the CustomDrawing protocol. UIView methods are available since the protocol extension is constrained to UIView and subtypes.

Related

Is a blank function conventional in subclass that conforms to custom protocol?

I have two main screens in my app, currently both just subclasses of UIViewController. These two view controllers are very similar - they both implement my custom subclass of UIView called HeaderView that is responsible for displaying information and taking user input. As it stands, this code is repetitive because the HeaderView setup is the same for both view controllers - the only difference is what happens when the user confirms the text entry in HeaderView.
To cut down on repetitive code, I am creating a class called InputViewController (a subclass of UIViewController) that houses the aspects of the two view controllers that are identical. Eventually, I want the two view controllers to subclass InputViewController instead of UIViewController.
class InputViewController: UIViewController, InputProtocol {
private let headerView = HeaderView()
override func viewDidLoad() {
super.viewDidLoad()
// layout, etc.
setupCallbacks()
}
internal func setupCallbacks() {
headerView.onUpdate = { (text: String) in
// called when user confirms text entry in headerView
self.onHeaderUpdate()
}
}
internal func onHeaderUpdate() {} // Blank function
}
setupCallbacks() and onHeaderUpdate() are methods defined in the protocol that the InputViewController conforms to. The HeaderView implements a callback closure that is handled in setupCallbacks() by headerView.onUpdate...
The protocol that InputViewController conforms to:
protocol InputProtocol {
func setupCallbacks()
func onHeaderUpdate()
}
To illustrate this, I drew up a diagram;
Since I want the subclasses of InputViewController to override the onHeaderUpdate() method, is it conventional to leave the definition of onHeaderUpdate() in InputViewController blank or is there another solution to this?
is it conventional to leave the definition of onHeaderUpdate() in InputViewController blank
Yes, that is called an abstract method. It is common to give it code that crashes deliberately, as a way of saying, “I exist only to be overridden in a subclass.”
(I should go further and say that what you are creating, a base view controller that carries out initial configurations that all subclasses must implement, is also normal.)

How to pass UIControl as a function parameter?

For example, I have one function. In this function as a parameter I have to pass some UIControl like UILabel, UITextFile, UIButton, etc. How can I do this:
func passDicData(controll : UIControll){
//code
}
Used like:
passDicData(UILabel)
//Use UIView
func passDicData(control:UIView){
//code
}
passDicData(UILabel(frame: .zero))
It is UIControl, not UIControll.
Moreover, the common ancestor of the UILabel, UITextField, and UIButton is UIView, see for example this scheme.
func passDicData(view: UIView){
//code
}
You can use this way,
func passDicData(control: AnyClass){
print(control.class())
}
and call it like this way,
passDicData(control: UILabel.self)
Yes, you can use UIControl as a parameter:
func passDicData(controll : UIControl){
//code
}
However, you will NOT be able to call this function like this:
passDicData(UILabel)
That's because
In your example, you are not creating an instance of an UILabel, you are just using its type. In order to pass the instance you should initialize an UILabel first, e.g., passDicData(UILabel())
UILabel is not a subclass of UIControl
Instead you could use UIView or UIResponder, both of them are super classes of UILabel
func passDicData(responder : UIResponder){
//code
}
passDicData(UILabel())

iOS , Handle tableview property globally

I'm using tableview in all classes of my project. I just want to remove some default property like table bouncing to all tableview. Instead of remove it in each and every class, is there any option to write single line code that reflects for tableview in all class?
You can use Extension like this.
extension UITableView{
func setTableViewBasicProperties(){
self.backgroundColor = UIColor.gray
//tableview's other propeties
}
}
and simply call method into your ViewController:
yourtableView.setTableViewBasicProperties()
You can extend UITableView class and modify the properties as well as add some methods for added functionality.Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code.
You can extend UITableView as
extension UITableView{
func configureTableView(){
self.alwaysBounceVertical = NO;
//modify other propeties of UITable View
}
}
Now you can set these properties to each of tableview in any class as
self.yourtableView.configureTableView()

Subclassing UIView

I see this topic is discussed elsewhere but I don't see an answer to my questions.
I subclassed UIView to create a custom view. Currently I'm using interface builder to create a UIView and then setting the custom class option to my subclass.
First question. When I do that, how to I reference that instance from my code? I have a public function I would like to call that updates my view but I don't know how to call it from my view controller
Second question. I created an instance of the class from within my view controller just playing around and I noticed the public function I created isn't available with that instance. Can I create public functions when I inherit from UIView?
It is easy to do:
1)subclass UIView to create CustomView, add your public function,in your project:
import UIKit
class CunstomView: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
public func printHello() {
print("hello")
}
}
2)In your storyboard, drag a UIView into your vc, and set the class to CunstomView, you can see that in my red frame:
3)click the Show the Assistant Editor, and ctrl-drag the view to the vc, set the name custom:
4)then in your vc's viewDidload function, you call the public function:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var custom: CunstomView!
override func viewDidLoad() {
super.viewDidLoad()
custom.printHello()
}
}
5)the result:
First question. When I do that, how to I reference that instance from
my code? I have a public function I would like to call that updates my
view but I don't know how to call it from my view controller
A: A view cannot exist by itself in app. You need viewcontroller to handle the custom view. Then in that VC, you can refer the view as IBOutlet.
Second question. I created an instance of the class from within my
view controller just playing around and I noticed the public function
I created isn't available with that instance. Can I create public
functions when I inherit from UIView?
A: You can create public function of custom view, just declare them in the header file. Then import the header in your VC. Then u can access it.
You can try to update your view from IB with the mothod below.
Objective-C:
-(void)awakeFromNib {
[super awakeFromNib];
//do something
}
Swift
override func awakeFromNib() {
super.awakeFromNib()
}
Second question
Do you mean the custom view don't answer the function you create within the view controller?

Lazily Override IBOutlet Object's Properties

I have a custom UItextView subclass where I override canBecomeFirstResponder():
class MyTextView: UITextView {
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
override func canBecomeFirstResponder() -> Bool {
return false
}
}
I'm doing this to allow the data detected links (phone numbers are URLs) in a UITextView to function without any of the other text in the view to be selectable, but that is unrelated to the question.
canBecomeFirstResponder() is the only property/method I want to override, so subclassing seems like overkill. I use this custom class with a view created with Interface Builder. Is there a way I can lazily override an IBOutlet's of a UIKit object's properties? Something like this:
#IBOutlet weak var contactTextView: UITextView! {
override func canBecomeFirstResponder: Bool = {
return false
}
}
I do not want to use an extension on UITextView because I only want to override canBecomeFirstResponder() for a specific UITextView, not every one used in my project.
If you want to override, you have to subclass. These two concepts are connected. You cannot override without subclassing.
Your "like this" idea wouldn't work in any language. Basically, you are describing an anonymous subclass which is available in Java, for instance (not in Swift). However, that would have to be used during class instantiation, not when declaring a variable.
Of course, you could swizzle canBecomeFirstResponder with a method returning false in didSet but that's much more complicated than subclassing.
The answer is that with the current versions of swift, there is no way to do that.
Based on what you are describing, it might be possible that you could make your view controller implement UITextViewDelegate and implement textViewShouldBeginEditing to return false.
This is just a guess based on the fact that you're seemingly trying to override canBecomeFirstResponder to disallow typing.
That all being said, as was pointed out if you return false from canBecomeFirstResponder I think the expected behavior is that the UI element will no longer allow itself to capture user input. From your responses in the comments, it seems that its capturing user input for you anyway, but that might just be a the particular version of iOS you're running. It could also be that my understanding of the first responder chain is incorrect.

Resources