How do you call an IBOutlet element's initializer? - ios

I wrote the following subclass of UITextField:
var imageView: UIButton? = nil
var options: [String]? = nil
let padding = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 30)
override init(frame: CGRect) {
super.init(frame:frame)
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
//fatalError("init(coder:) has not been implemented")
}
}
When I add instances of this class programmatically, it works fine. But when I add an instance of the class in the storyboard and then connect it with an IBAction, it's just a blank white text field without any of the properties I assigned in the class's initializer - it seems that the initializer isn't being called at all. Is there any way to call the element's initializer? Or is there another function, similar to viewDidLoad, that will run when the text field is loaded?

You'll have to call the implementation given in init(frame:) in init(coder:) because this method is called when used from the storyboard. Here is the code:
override init(frame: CGRect) {
super.init(frame:frame)
initialSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialSetup()
}
func initialSetup() {
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}

You can't call the initialiser of the component added in storyboard. But you are going in right direction. Create a common method to set these properties.
func commonSetup() {
self.backgroundColor = Constants.darkPurple
self.textColor = .white
self.layer.cornerRadius = 10
self.clipsToBounds = true
self.translatesAutoresizingMaskIntoConstraints = false
self.tintColor = .clear
Constants.styleDropDownField(self)
}
And call this method from three different methods
override func awakeFromNib() {
super.awakeFromNib()
self.commonSetup()
}
override init(frame: CGRect) {
super.init(frame:frame)
self.commonSetup()
}
//This method will be called when component is initialised from storyboard.
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonSetup()
}

Related

Custom UIButton class does not show title

I have created a custom UIButton class. But the title does not show up. I only get the background color and the rounded corners. But the title is not visible.
Also when I run it in simulator, iOS 13+, its showing the title white. But when I run in my device i.e. iOS 12.4, its not working.
Here is my code:
public class CornerButton : UIButton {
public override init(frame: CGRect) {
super.init(frame: frame)
setup()
self.layoutIfNeeded()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
self.layoutIfNeeded()
}
func setup() {
self.titleLabel?.font = UIFont(name: "Poppins-SemiBold", size: 12)
self.backgroundColor = UIColor(named: "BarColor")
self.setTitleColor(.white, for: .normal) // this line is not working
self.clipsToBounds = true
}
public override func layoutSubviews() {
self.roundCorners(corners: [.topRight,.bottomLeft], radius: 10)
}
}
You need to call super.layoutSubviews()
public override func layoutSubviews() {
super.layoutSubviews()
self.roundCorners(corners: [.topRight,.bottomLeft], radius: 10)
}
BTW here you don't need layoutSubviews as you use it when you need to make something that depends on the actual measured bounds of the view and as you set a static radius you can omit it

UISearchBar's text move to bottom when input content?

I customized a UISearchbar, the code is like this:
class CustomSearchBar: UIView {
lazy var searchBar: MySearchBar = {
let search = MySearchBar()
search.tintColor = UIColor.orange
search.barTintColor = UIColor.yellow
search.searchBarStyle = .minimal
search.searchTextPositionAdjustment = UIOffset(horizontal: 10, vertical: 0)
return search
}()
lazy var cancelButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("取消", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
addSubview(searchBar)
addSubview(cancelButton)
}
private func setupConstraints() {
searchBar.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(20)
make.top.equalToSuperview().offset(6)
make.height.equalTo(32)
make.width.equalToSuperview().offset(-80)
}
cancelButton.snp.makeConstraints { (make) in
make.left.equalTo(searchBar.snp.right).offset(13)
make.centerY.equalTo(searchBar)
}
}
}
class MySearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func indexOfSearchFieldInSubviews() -> Int! {
var index: Int!
let searchBarView = subviews[0]
for (i, view) in searchBarView.subviews.enumerated() {
if view.isKind(of: UITextField.self) {
index = i
break
}
}
return index
}
override func draw(_ rect: CGRect) {
if let index = indexOfSearchFieldInSubviews() {
let searchField: UITextField = subviews[0].subviews[index] as! UITextField
searchField.leftView = UIImageView(image: UIImage(named: "Search_icon"))
searchField.font = UIFont.systemFont(ofSize: 14)
searchField.borderStyle = .none
searchField.layer.masksToBounds = true
searchField.layer.cornerRadius = 4
searchField.backgroundColor = barTintColor
searchField.contentVerticalAlignment = .center
searchField.placeholder = "input something"
}
}
}
we can see that it is very simple. Just added a searchBar and cancelButton.
Then I added it to navigationBar as subView, make it becomeFirstResponder in viewController.
class ViewController: UIViewController {
lazy var customeSearchBar = CustomSearchBar()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.addSubview(customeSearchBar)
customeSearchBar.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.centerY.equalToSuperview()
make.height.equalTo(44)
}
customeSearchBar.searchBar.becomeFirstResponder()
}
}
At first, I input content is ok.
When I continue input content, then the issue shows up:
It looks like the content move to the bottom. I tried to find something to fix the problem. But I didn't get it.
It is a strange thing that works fine in simulate and some iPhone. But one of my phone works wrong.

Rounded UIButton - iPad size

I have custom UIButton to have rounded edges
import UIKit
class RoundedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.layer.cornerRadius = self.bounds.width * 0.5
self.backgroundColor = UIColor(patternImage: UIImage(named: "map.png")!)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = self.bounds.width * 0.5
self.backgroundColor = UIColor(patternImage: UIImage(named: "map.png")!)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
}
Now I am using this "RoundedButton" (size of 110, 110) in a UIViewcontroller's XIB file and the constraints are set to maintain the aspect ratio w.r.t UIViewcontrollers view.
The button looks rounded in iPhone simulators but the button is NOT rounded in iPad simulator. When I set the layer.cornerRadius property in viewDidAppear then the button is rounded in iPad simulator.
Please see images
I am looking for an alternative solution than re-defining layer corner radius again in viewDidappear.
Thanks
override method layoutSubviews like this:
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.bounds.width * 0.5
}
Also you can now remove cornerRadius line from init
For more go to documentation of UIView: https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews
Override layoutSubviews function:
import UIKit
class RoundedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor(patternImage: UIImage(named: "map.png")!)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor(patternImage: UIImage(named: "map.png")!)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.bounds.width * 0.5
}
}
import UIKit
class RoundedButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setTitleColor(UIColor.white, for: UIControlState.normal)
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = self.bounds.width / 2
}
}
I have also create a code sample for you. RoundedButton

Title not showing for custom UIButton class

I have created a custom UIButton class which features a round frame. The problem is although I set up the title property of each button from the storyboard they are not showing. I end up with round buttons with no titles showing inside them. I am trying to do a keypad like the one in default iOS passcode screen. My main view background color is not white and I am not using any background images for the buttons. Here is my code for the custom UIButton class.
import Foundation
import UIKit
class MyOwnButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
self.clipsToBounds = true
self.titleLabel?.font = UIFont.systemFontOfSize(33.0)
self.titleLabel?.textColor = UIColor.whiteColor()
self.layer.cornerRadius = self.frame.size.width / 2.0
self.layer.borderColor = UIColor.whiteColor().CGColor
self.layer.borderWidth = 2.0
}
}
Had same issue, this answer worked for me: Custom UIButton subclass not displaying title
Add super.layoutSubviews() after you override the function.
Have you set your UIButton's class as your custom class in storyboard??
Try to put all the things in init method and check.
import Foundation
import UIKit
class MyOwnButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
self.clipsToBounds = true
self.titleLabel?.font = UIFont.systemFontOfSize(33.0)
self.titleLabel?.textColor = UIColor.whiteColor()
self.layer.cornerRadius = self.frame.size.width / 2.0
self.layer.borderColor = UIColor.whiteColor().CGColor
self.layer.borderWidth = 2.0
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Here is working code for Swift 4
import UIKit
class MyOwnButton: UIButton {
override init(frame: CGRect){
super.init(frame: frame)
commonSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonSetup()
}
private func commonSetup() {
self.clipsToBounds = true
self.titleLabel?.font = UIFont.systemFont(ofSize: 33.0)
self.titleLabel?.textColor = UIColor.white
self.layer.cornerRadius = self.frame.size.width / 2.0
self.layer.borderColor = UIColor.white.cgColor
self.layer.borderWidth = 2.0
}
}
After setting the title of the button, simply:
[button sizeToFit];

Instantiate custom button from code and Storyboard - how to make an init method

I would like to create a button like this:
import UIKit
class EKLikeButton: UIButton {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.layer.cornerRadius = 5.0;
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 1.5
self.backgroundColor = UIColor.blueColor()
self.tintColor = UIColor.whiteColor()
}
}
but the only way to make it seems to be to set a pre-existing button in a Storyboard. I'd like to be able to do:
let btn = EKLikeButton()
btn.frame=CGRectMake(10.0, 10.0, 40.0, 40.0)
but when I try the above, I get
Missing argument for parameter 'coder' in call
How would I make an init function that can handle both from code or from storyboard in Swift?
This is what I usually do
class EKLikeButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
init(){
super.init(frame: CGRectZero)
setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
func setUp(){
self.layer.cornerRadius = 5.0;
self.layer.borderColor = UIColor.redColor().CGColor
self.layer.borderWidth = 1.5
self.backgroundColor = UIColor.blueColor()
self.tintColor = UIColor.whiteColor()
}
}
That error message is telling you that it's looking for that coder param, because you only have that one init function. You haven't declared an initializer with no parameters, so you can't init like: EKLikeButton()
To add an init that accepts a frame parameter, you need to also implement:
override init(frame: CGRect) {
super.init(frame: frame)
// set up your frame, init, whatever
}
Then you can instantiate it like this:
let btn = EKLikeButton(CGRect(10, 10, 40, 40))

Resources