I have created a custom UIButton to use programmatically in my app. On one screen it works fine. On another, the background does not show up. I have looked up many similar questions and also compared the code to the other View Controller it's used in when it works and there are no obvious reasons. Why is the background color not showing?
The Custom Button Class
import Foundation
import UIKit
class PillButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
initializeButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initializeButton()
}
private func initializeButton() {
backgroundColor = UIColor.white
setTitleColor(UIColor(named: "pink"), for: .normal)
contentEdgeInsets = UIEdgeInsets.init(top: 16, left: 48, bottom: 16, right: 48)
translatesAutoresizingMaskIntoConstraints = false
}
override func layoutSubviews() {
super.layoutSubviews()
let height = frame.height / 2
layer.cornerRadius = height
}
}
The View Controller
import Foundation
import UIKit
import MaterialComponents
class EventViewController: BaseViewController {
private static let HORIZONTAL_PADDING: CGFloat = 16
private var confirmButton: PillButton!
private var unableToAttendButton: UILabel!
private var signedUpLabel: UILabel!
private var baseScrollView: UIScrollView!
var event: Event!
private var viewModel: EventViewModel = EventViewModel()
override func viewDidLoad() {
super.viewDidLoad()
createView()
}
override func createView() {
super.createView()
createConfirmButton()
}
private func createConfirmButton() {
confirmButton = PillButton()
let descriptionBottomGuide = UILayoutGuide()
baseScrollView.addSubview(confirmButton)
baseScrollView.addLayoutGuide(descriptionBottomGuide)
descriptionBottomGuide.topAnchor.constraint(equalTo: eventDescription.bottomAnchor).isActive = true
confirmButton.centerXAnchor.constraint(equalTo: baseScrollView.centerXAnchor).isActive = true
confirmButton.topAnchor.constraint(equalTo: descriptionBottomGuide.bottomAnchor, constant: 20).isActive = true
}
}
The code you posted has a LOT of information that you didn't provide, so it's pretty difficult to know what might be going on.
That said, you have a few issues with your PillButton class:
you should not be calling initializeButton in layoutSubviews()
you should update the corner radius in layoutSubviews()
no need to override setTitle
no need to set the layer background color, and you've already set the button's background color so no need to set it again.
Also, in the code you posted, you're not setting the button title anywhere.
Try replacing your PillButton class with this one, and see if you get better results:
class PillButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
initializeButton()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
initializeButton()
}
private func initializeButton() {
backgroundColor = Colors.black
setTitleColor(UIColor(named: "pink"), for: .normal)
contentEdgeInsets = UIEdgeInsets.init(top: 16, left: 48, bottom: 16, right: 48)
translatesAutoresizingMaskIntoConstraints = false
}
override func layoutSubviews() {
super.layoutSubviews()
// update corner radius here!
layer.cornerRadius = bounds.height / 2
}
}
If you don't, then you need to do some debugging through the rest of your code (that you have not posted here) to find out what's going on.
confirmButton = PillButton()
I would look into this piece of code. The designated initializers, the ones with frame and coder, in the custom button class call initializeButton(), but you are not implementing init() to do the same.
I would change it to confirmButton = PillButton(frame:)
Related
I have set up a subclassed UIView, and want to see the embedded image in IB - so I've set it as IBDesignable
#IBDesignable
class DieView: UIView {
#IBInspectable
var dieImage : UIImage = UIImage()
override init(frame: CGRect) {
super.init(frame: frame)
updateLayout()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
updateLayout()
}
// for IB
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
updateLayout()
}
func updateLayout() {
self.backgroundColor = .red
let profileImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
profileImageView.image = UIImage(named: "dice1")
profileImageView.contentMode = .scaleAspectFit
profileImageView.layer.masksToBounds = false
self.addSubview(profileImageView)
}
func showNumber(number: Int) {
}
}
The background colour changes, but the embedded image doesn't update. Why not?
From the documentation of the prepareForInterfaceBuilder():
Interface Builder waits until all objects in a graph have been created
and initialized before calling this method. So if your object’s
runtime configuration relies on subviews or parent views, those
objects should exist by the time this method is called.
which says that subviews should exist before this method is called. I'm not sure, but try to add image view before this is called. Also, you have to keep in mind that prepareForInterfaceBuilder() is called independently by interface builder. Read the docs for more info. Good Luck!
Hi,
It would be much appreciated if someone could help me out with my below question.
I created a UINavigationBar class, which I created an instance from in the ViewController that I would like to implement it in. However, the title for the NavigationBar is not appearing where I want it to. As illustrated the title is currently getting displayed at the very top of the NavigationBar (i.e., in the dark blue area). How can I make the title appears in the light blue color area? And how can I modify its position within the NavigationBar area as desired?
Thanks,
Shadi.
import UIKit
import ChameleonFramework
class CustomNavigationBar: UINavigationBar {
override init(frame: CGRect) {
super.init(frame: frame)
setupNavigationBar()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupNavigationBar()
}
func setupNavigationBar() {
isTranslucent = true
backgroundColor = .blue
barStyle = .blackTranslucent
var fsafdsaf = topItem?.title
fsafdsaf = "dsadfsadfsafsdafsad"
let titlfsdfe = UINavigationItem.init(title: fsafdsaf!)
setItems([titlfsdfe], animated: false)
}
}
Well I managed to sort out my issue, however, any further alternatives are much welcomed.
Basically, I added a UILabel to my Navigation Bar in my UINavigationBar Class and managed to sort out the issue.
Refer to below code for solution:
import UIKit
import ChameleonFramework
class CustomNavigationBar: UINavigationBar {
override init(frame: CGRect) {
super.init(frame: frame)
setupNavigationBar()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupNavigationBar()
}
func setupNavigationBar() {
isTranslucent = true
backgroundColor = .blue
barStyle = .blackTranslucent
let navigationBarTitleFrame = CGRect(x: 200, y: 50, width: 120, height: 25)
let navigationBarTitle = UILabel(frame: navigationBarTitleFrame)
navigationBarTitle.text = "This is my title"
navigationBarTitle.textAlignment = .center
navigationBarTitle.textColor = .white
navigationBarTitle.layer.borderWidth = 3.0
self.addSubview(navigationBarTitle)
}
}
I know it may be the basic question but I am new to Swift.
Also, I have tried various solutions on SO but could not resolve the issue.
So if anyone can help me with my problem.
I have a custom UIVIEW class as follows:
class SearchTextFieldView: UIView, UITextFieldDelegate{
public var searchText = UITextField()
override init(frame: CGRect) {
super.init(frame: frame)
initializeUI()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeUI()
}
func initializeUI() {
searchText.placeholder = "Enter model no"
searchText.backgroundColor = UIColor.white
searchText.textColor = UIColor.darkGray
searchText.layer.cornerRadius = 5.0
searchText.delegate=self
self.addSubview(searchText)
}
override func layoutSubviews() {
searchText.frame = CGRect(x: 20.0, y: 5.0, width: self.frame.size.width - 40,height : self.frame.size.height - 10)
}
}
Now I want to set text to SearchText textfield from another class which is as follows:
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
// Do any additional setup after loading the view.
}
func setupUI() {
let searchTextFieldView = SearchTextFieldView()
self.view.addSubview(searchTextFieldView) //adding view containing search box view at the top
**searchTextFieldView.searchText.text = "My Text"**
}
I am using Storyboard. Also, I can see the textfield with placeholder text.only problem is I can not set text to it.
Can anybody help. Whats wrong in my code.
It is needed to call searchTextFieldView.setNeedsDisplay(), this will in turn call override func draw(_ rect: CGRect) in class SearchTextFieldView.
Add override func draw(_ rect: CGRect) {} in SearchTextFieldView, and try setting searchText.text = <someValue> in draw(). You can use a String property in SearchTextFieldView, to get <someValue> from the client (one who is using SearchTextFieldView) class.
You are creating you view via SearchTextFieldView(), while you have 2 available initializers init(frame:) and init?(coder:).
If you change
let searchTextFieldView = SearchTextFieldView()
with
let searchTextFieldView = SearchTextFieldView(frame: CGRect(x: 50, y: 50, width: 100, height: 100))
you will see the text.
You are not setting frame to the view. Also you are not loading the .xib in the view class. It should be like:-
class SearchTextFieldView: UIView, UITextFieldDelegate{
//MARK:- Initializer
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize(withFrame: self.bounds)
}
override init(frame : CGRect) {
super.init(frame: frame)
initialize(withFrame: frame)
}
//MARK: - View Initializers
func initialize(withFrame frame : CGRect) {
Bundle.main.loadNibNamed("SearchTextFieldView", owner: self, options: nil)
view.frame = frame
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view)
initializeUI()
}
}
Now you can call the below code in view controller:-
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
// Do any additional setup after loading the view.
}
func setupUI() {
let searchTextFieldView = SearchTextFieldView(frame: ?*self.view.bounds)
self.view.addSubview(searchTextFieldView)
//adding view containing search box view at the top
searchTextFieldView.searchText.text = "My Text"
}
Don't forget to create an xib with name "SearchTextFieldView.xib" as you are loading that nib in your initialize function.
Hope it helps :)
add frame for the searchTextFieldView inside setupUI() method. because the View got loaded on the view but its doesn't have a frame (x,y position, width and height). Change your UIViewController's colour to grey and u can see the your view loaded on the left corner (0,0). set frame size for the view that will solve this problem.
I have a button that repeats throughout my app, so I created a subclass to avoid having to set all the basic properties every time.
I am able to set the background colour, the text colour, round the corners.
However, things fall apart when I try to set a default title - something other than "Button".
In Interface Builder it ignores the title, but it also then ignores the font colour, which works when I don't set the title.
If I run the app, it all looks fine, but one major point of using Interface Builder is to save the step of constantly running the app to check basic UI layout.
Here is the subclass.
Note that if you comment out the 2 setTitle lines, the button shows the correct text colour (white).
import UIKit
#IBDesignable class ContinueButton: UIButton {
#IBInspectable var titleColour: UIColor = .white {
didSet {
setTitleColor(titleColour, for: .normal)
}
}
#IBInspectable var bgColour: UIColor = UIColor.gray {
didSet {
backgroundColor = bgColour
}
}
#IBInspectable var buttonTitle: String = "Continue" {
didSet {
setTitle(buttonTitle, for: .normal)
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setAttributes()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setAttributes()
}
public func setAttributes() {
setTitleColor(titleColour, for: .normal)
backgroundColor = bgColour
setTitle(buttonTitle, for: .normal)
}
override public func layoutSubviews() {
super.layoutSubviews()
setAttributes()
layer.cornerRadius = 0.5 * bounds.size.height
clipsToBounds = true
}
}
ps, My main objective is to create a reusable custom button that takes care of setting a bunch of defaults. If there's a better way to achieve that, I'd be very happy to hear that - especially if it could be done visually rather than through code.
Thanks for any advice you can give,
-Nico
Just override prepareForInterfaceBuilder() and add setAttributes().
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setAttributes()
}
The tintColor property will do the trick.
button.tintColor = UIColor.red
but to make sure to override the prepareForInterfaceBuilder()
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
button.tintColor = UIColor.red
}
I need to open keyboard on button click for UIButton (not using/for UITextField). I have tried to create custom button by overriding variable canBecomeFirstResponder but it's not working.
Is there any other way to do so?
Note: I want to set UIPIckerView as an input view of UIButton in key board frame.
Here is my code.
class RespondingButton: UIButton {
override var canBecomeFirstResponder: Bool {
return true
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
// common init
}
}
In my view controller, I connected button action.
class TestViewController: UIViewController {
#IBAction func testBecomeFirstResponder(button: RespondingButton){
button.becomeFirstResponder() // Not working.
}
}
Here is what I would do.
Create transparent textField 1x1px, lets say it is myTextField.
Then add your desired button. In the button action make the myTextField.becomeFirstResponder().
Create view:
let pvBackground: UIView = {
let v = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
v.backgroundColor = .white
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
In viewDidLoad:
pvBackground.addSubview(yourPickerView)//add the picker into the pvBackground
myTextField.inputView = pvBackground
I added the pickerView into the another view to be able to customize it more.
Add conformance to UIKeyInput like this. It should work.
class RespondingButton: UIButton, UIKeyInput {
override var canBecomeFirstResponder: Bool {
return true
}
var hasText: Bool = true
func insertText(_ text: String) {}
func deleteBackward() {}
}