Can't design subview of my #IBDesignable view - ios

I have the following:
#IBDesignable class CustomView: UIView {
#IBInspectable var hasFlag: Bool = true
var flagView: UIView?
override init(frame: frame) {
super.init(frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
if (hasFlag) {
flagView = UIView()
flagView!.translatesAutoresizingMaskIntoConstraints = false
flagView!.backgroundColor = UIColor.redColor()
addSubview(flagView!)
flagView!.leadingAnchor.constraintEqualToAnchor(leadingAnchor, constant: 0).active = true
flagView!.topAnchor.constraintEqualToAnchor(topAnchor, constant: 0).active = true
flagView!.widthAnchor.constraintEqualToConstant(8).active = true
flagView!.bottomAnchor.constraintEqualToAnchor(bottomAnchor, constant: 0).active = true
}
}
}
However, when I go into interface builder, I see my red flag there but I don't have the option to select the flagView to, say, set its background color. Nor do I see it appear in the view hierarchy under my CustomView. I'm going to have multiple CustomViews, and each one might have a different color. I'd like to do this in interface builder rather than in code. Is there a way I can achieve editing my subview of my #IBDesignable through interface builder?

You can try "setup()" in layoutSubviews.
override func layoutSubviews() {
super.layoutSubviews()
setup()
}

Related

Custom UIButton background color not working

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:)

UITextView does not change textColor property

I have a UITextView custom class:
class TitleTextView: UITextView {
override func layoutSubviews() {
super.layoutSubviews()
setup()
}
func setup() {
textContainerInset = UIEdgeInsets.zero
textContainer.lineFragmentPadding = 0
textColor = .brand100
backgroundColor = .clear
isUserInteractionEnabled = false
textAlignment = .left
isScrollEnabled = false
let frameWidth = Constants.screenSize.width * 87.5 / 100
font = UIFont.OpenSans(.semibold, size: (frameWidth * 8.55 / 100))
}
}
I used this text view custom class inner a UIView.
class MyCustomHeaderView: UIView{
#IBOutlet weak var titleTextView: TitleTextView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
backgroundColor = .brand100
titleTextView.text = "Market Place"
titleTextView.textColor = .brand400
layoutIfNeeded()
}
}
And I called this UIView in a UIViewController.
private func setupTitleView() {
let titleView = UINib(nibName: "TitleView", bundle: .main).instantiate(withOwner: nil, options: nil).first as! UIView
titleView.frame = contentHeaderView.bounds
contentHeaderView.addSubview(titleView)
view.layoutIfNeeded()
}
But when I set the textColor property in my custom UIView (MyCustomHeaderView) the color doesn't change.
Do you have any idea about why the reason that my UITextView doesn't apply the color that I set in my custom UIView?
 
I called layoutIfNeed() but this doesn't work.
It's because you are doing everything inside the layoutSubviews Which in itself is really bad practice.
In your case you instantiate the CustomHeaderView and the layout for that is called, hence calling layoutSubviews next step is that the textView is added to your CustomHeaderView and then the textView's layoutSubviews is called and will override your color.
You can solve this in two ways i believe. Altho i don't work with Nibs and storyboards,
first:
class MyCustomHeaderView: UIView{
#IBOutlet weak var titleTextView: TitleTextView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
backgroundColor = .brand100
titleTextView.text = "Market Place"
titleTextView.textColor = .brand400
layoutIfNeeded()
}
}
Second, this is a big maybe:
class MyCustomHeaderView: UIView{
#IBOutlet weak var titleTextView: TitleTextView!
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
defer {
backgroundColor = .brand100
titleTextView.text = "Market Place"
titleTextView.textColor = .brand400
layoutIfNeeded()
}
}
}
Defer will wait till everything is been initialised before running whatever is in the block. I don't know tho how that works with layoutSubviews

How to see changes when adding Custom Class to UILabel, in storyboard itself?

I have added a custom class to UILabel.
Custom Class is:
#IBDesignable class CustomLabel: UILabel {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
}
private func setup() {
self.textColor = UIColor.blue
}
}
But I cant see the changes in storyboard. How can it is able to see the changes in the interface builder??
You have to add IBInspectable properties to see changes in storyboard
here is example
#IBDesignable class RoundedTextField: UITextField {
#IBInspectable var cornerRadius:CGFloat = 0 // You will see this in storyboard
#IBInspectable var borderColor:UIColor = .green // You will see this in storyboard
override func awakeFromNib() {
super.awakeFromNib()
self.borderStyle = .none
self.layer.cornerRadius = cornerRadius
self.layer.borderColor = borderColor.cgColor
self.layer.borderWidth = 2.0
self.backgroundColor = UIColor.white
self.layer.masksToBounds = true
}
}
Don't forgot to set class
Here how you can see in stoyrboard

Referencing superview inside of view code class file

I'm currently trying to learn constraints and styling programmatically in Swift. I'm also trying to maintain clean and modularized code by splitting up code that relates to "styling".
I simply have my LoginViewController:
import UIKit
class LoginViewController: UIViewController {
var loginView: LoginView!
override func viewDidLoad() {
super.viewDidLoad()
loginView = LoginView(frame: CGRect.zero)
self.view.addSubview(loginView)
// AutoLayout
loginView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets.zero, excludingEdge: .bottom)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
Then my LoginView:
import UIKit
class LoginView: UIView {
var shouldSetupConstraints = true
var headerContainerView: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
// Header Container View
headerContainerView = UIView(frame: CGRect.zero)
headerContainerView.backgroundColor = UIColor(red:0.42, green:0.56, blue:0.14, alpha:1.0) // #6B8E23
headerContainerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(headerContainerView)
headerContainerView.topAnchor.constraint(equalTo: self.superview!.topAnchor)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func updateConstraints() {
if(shouldSetupConstraints) {
// AutoLayout constraints
shouldSetupConstraints = false
}
super.updateConstraints()
}
}
Where I am getting stuck is with just simply trying to add this headerContainerView to the top of my superview. I want to be able to add it so it pins itself to the top, left and right of the superview and only 1/3 of the superview's height. I continue to try and reference the superview with no success and I cannot find a solution that helps me understand on the internet. Any suggestions on how I can complete this?
Thank you for taking the time for those that respond.
NOTE: I did start out using PureLayout which is really nice. However, I am an individual that likes to understand what is going on behind the scenes or at least how to write the code at its base level. You can see that I am using a PureLayout function in my LoginViewController, but I am looking to change that. I would prefer a solution that doesn't add a third party library.
Here self in the custom UIView class is the parent view of headerContainerView so , You can add this , Also I recommend to learn constraints first without 3rd party libraries to fully understand the concept as you will learn a lot from seeing conflicts and other things , once done , shift to libraries
override init(frame: CGRect) {
super.init(frame: frame)
// Header Container View
headerContainerView = UIView(frame: CGRect.zero)
headerContainerView.backgroundColor = UIColor(red:0.42, green:0.56, blue:0.14, alpha:1.0) // #6B8E23
headerContainerView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(headerContainerView)
headerContainerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
headerContainerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
headerContainerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
headerContainerView.heightAnchor.constraintEqualToAnchor(self.heightAnchor, multiplier:1.0/3.0, constant: 0.0).active = true
}
// loginView layout
override func viewDidLoad() {
super.viewDidLoad()
loginView = LoginView(frame: CGRect.zero)
self.view.addSubview(loginView)
loginView.translatesAutoresizingMaskIntoConstraints = false
loginView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
loginView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
loginView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
loginView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
headerContainerView.snp.makeConstraints { (make) in
make.top.equalTo(self)
make.leading.and.trailing.equalTo(self)
make.height.equalTo(self.frame.height/3)
}
With SnapKit.
With SnapKit, you can do the following:
override func viewDidLoad() {
super.viewDidLoad()
loginView = LoginView(frame: CGRect.zero)
self.view.addSubview(loginView)
// AutoLayout
loginView.snp.makeConstraints { (make) in
make.left.equalTo(view.snp.left)
make.right.equalTo(view.snp.right)
make.top.equalTo(view.snp.top)
make.height.equalTo(view.snp.height).multipliedBy(1/3)
}
}

How to custom this image tab in UIView,using #IBDesignable and #IBInspectable is better

How to draw the following tab image in UIView?The text is changeable,which implies that the image could stretch in width. And I know in XCode6,it supports live render.So I think if possible,it's color,text,and size could be set in attributes inspector.
You have to create a custom class, based on UIView. This class is declared as #IBDesignable and has #IBInspectable properties. Override UIView.drawRect() and you are totally free on how your view gets displayed.
Here is a sample class to get you started.
import UIKit
#IBDesignable
class MyTabView: UIView {
#IBInspectable tabTitle: String = ""
#IBInspectable tabColor: UIColor = UIColor.clearColor()
override init(frame: CGRect) {
super.init(frame: frame)
// Initialization code
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func prepareForInterfaceBuilder() {
// stuff for interface builder only
}
override func drawRect(rect: CGRect)
{
// this is where your view gets drawed
self.layer.cornerRadius = 10
self.layer.masksToBounds = true
}
}

Resources