Custom Class View for UIButton - ios

In Identity Inspector, I want to set a custom class for my UIButton.
Thus I create my custom class that is a UIView but my custom class doesn't appears on the list.
Vice-versa, if I add a UIView, on the list appears most class included UIButton.
If UIButton is a subclass of UIView, and not a UIView is a UIButton,
why is it possible and why can't I use my UIView custom class for representing my UIButton?

I'm not really sure entirely what you mean but I'll have a go.
Your question ...
If UIButton is a subclass of UIView, and not a UIView is a UIButton, why it's possible and why I can't use my UIView custom class for representing my UIButton.
I believe you are saying...
UIButton is a subclass of UIView.
Your custom view is a subclass of UIView.
Why can't you set your UIButton to be your custom view?
Is that right?
If so, it's fairly clear. Just because they are derived from the same class does not mean they are related. A UILabel is also derived from UIView but is very different.
If you want to create a custom subclass of UIButton then your custom class should derive from UIButton...
You should have...
#interface YourSubclass: UIButton
Instead of...
#interface YourSubclass: UIView
Doing this will make the custom subclass appear in the Identity Inspector in Interface Builder.

You can create a custom subclass for a UIButton. Then set it as Custom Class for any button.
class CCButton: UIButton {
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
createBorder()
}
required override init(frame: CGRect) {
super.init(frame: frame)
createBorder()
}
private func createBorder(){
//Define Layer
self.layer.cornerRadius = 8.0
self.layer.masksToBounds = true
}
}

Related

UIView not showing elements in ViewController Swift 4

I have a UIView class. And assigned it to a UIView inside of a ViewController.
I am animating the drawing of a circle in the UIView.
How do I load the UIView in the ViewController?
ViewDidLoad doesn’t work or gives errors.
LoadView also doesn’t work.
Create class for your view controller lets say ParentController subclass of UIViewController.
Create outlet of your UIView in ParentController.
Instead of creating class for UIView define function in ParentController to draw circle and call it in ParentController.
After function call show your add your UIView to ParentController
e.g - self.view.addSubview(self.cirecleUIview)
I personally suggest
1) create a file for your class, say MyView.
2) create a NIB/XIB, too, so you can eventually add other elements.
3) add a custom element (of class MyView) in controller in Main storyboard
4) Add outlet in your controller to your view (so you can fill/set/access it..)
5) NEVER call directly drawing functions (will be automatic.. or triggered manually using setNeedsUpdate..)
6) override init/awake methods, so you can load from resources or code:
override init(frame: CGRect) {
super.init(frame: frame)
prepare()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
final private func prepare(){
// your "init" code here..
}
6) implement you drawing code:
override func draw(_ rect: CGRect) {
// stupid code.. will fill in red...
if let context = UIGraphicsGetCurrentContext(){
context.setFillColor(UIColor.red.cgColor)
context.addRect(rect)
context.fillPath()
}
}

How to find the base class which called the Decoder initialiser

I have a BaseView class which is inherited from UIView like:
#IBDesignable class BaseView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I set this class for UILabel, UIButton and UITextField in storyboard.
Now, I have to recognise that which one of the UILabel, UIButton or UITextField is called the init?(coder aDecoder: NSCoder).
Is there anyway around this issue?
Update:
It seems that it's not possible to set custom class of type UIView to UILabel so there is no way around this issue.
I tried to test the class
if self is UILabel { }
But I get a compiler warning that cast from BaseView to unrelated UILabel always fail
if (self as UIView) is UILabel { }
clears the warning but is of no use.
So a question: you said "I set this class for UILabel, UIButton and UITextField in storyboard"
Could you detail what you did ?
How do you declare the IBOutlet ? BaseView ? What type do you assign in IB to the label ?
Because BaseView is not a subclass of UILabel, I could not declare the label as BaseView ?

What is the steps to create a subclass for UIView that adds border? [duplicate]

This question already has answers here:
Proper practice for subclassing UIView?
(4 answers)
Closed 6 years ago.
In my current project, I often create a UIView to put a grey rectangle on the view. I usually put white views first on the layout, and then set all of the border in the viewDidLoad(). Now I decided that I want to speed things up by writing a subclass that will automatically set the border of the view, and then set all those views to use that subclass. But I don't know where to put this code on the subclass:
self.layer.borderWidth = 2;
self.layer.borderColor = UIColor.grayColor().CGColor;
Do I put it on override init()? Do I need to override every version of init for the UIView? Or is there a better way to do this?
Thanks.
PS: if there's also any way to make that the border can be immediately shown on the storyboard design time (I think it has something to do with drawable but I don't understand at all about it), I'll be very grateful!
EDIT:
From the accepted answer, I get this answer: https://stackoverflow.com/a/33721647/3003927 which basically like this:
import UIKit
class MyView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
didLoad()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
didLoad()
}
convenience init() {
self.init(frame: CGRectZero)
}
func didLoad() {
//Place your initialization code here
self.layer.borderWidth = 2;
self.layer.borderColor = UIColor.grayColor().CGColor;
}
}
You need to create subclass of UIView and declare IBInspectable properties as per your needs in this class .
Try below links as example :
http://nshipster.com/ibinspectable-ibdesignable/
https://www.captechconsulting.com/blogs/ibdesignables-in-xcode-6-and-ios-8
There's a pretty detailed answer here:
Proper practice for subclassing UIView?
Basically, you should override:
init?(coder aDecoder: NSCoder) and init(frame: CGRect) as well as awakeFromNib(). Just call another function from there where you set the border.

How to create a style or theme on swift so I can set color to all UITextField

It seems to be pretty obvious but I couldn't find much about it after hours.
I'm build an App on swift 2.0 and I have a dark background to all views.
I want to set a common background and color to all my text views, people recommend that each view should set the properties on viewDidLoad, but I don't want to set to each textView one by one the same information. It will be a hell to maintain.
Any hints?
Technically yes, each one should be done separately so that you have more control over things. However, if you want to change the background color of all of your text fields you can do it like this:
UITextField.appearance().backgroundColor = //Your color
You can also change the caret color using this:
UITextField.appearance().tintColor = //Your color
If you want to change these colors for a UITextView then simply swap UITextField.appearance()... for UITextView.appearance()...
Subclass UITextView and set the properties to what you desire in the init method. Then have each of your UITextView have a Custom Class set to your UITextView subclass.
Example:
class UITextViewSubclass: UITextView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
backgroundColor = UIColor.whiteColor()
}
}
With this approach, you could also declare some #IBInspectable properties which means they can be modified from the Inspector, if you want to change this color from the Inspector instead of changes inside your code.
class UITextViewSubclass: UITextView {
#IBInspectable var customBackgroundColor: UIColor = UIColor.whiteColor() {
didSet {
backgroundColor = customBackgroundColor
}
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
backgroundColor = customBackgroundColor
}
}
One approach is to use UIAppearance with to change all UITextView, or you can subclass UITextView and modify the object after initialisation, then use that subclass instead of UITextView.

How to make reusable UIView template consisiting UILabels

I'm building a weather app as a beginner project. Say I wanted a custom view that consisted on many UILabels for temp, humidity, precipitation, etc. The idea is that this custom UIView would be used several times for every city the user has saved. If the user has 3 cities saved, the custom view would have 3 instances.
What is the best way to do this? I'm trying to subclass a UIView. Originally I was overriding drawRect(rect: CGRect) and defining my UILabels there. That just didn't feel right. And it wouldn't get alloc/inited until way later, after I was trying to update the label text in the completion handler on NSURLSession.
Or should I be overriding init() which makes me do this:
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
And I have no idea what that means. Then I'm forced to doing something like this when I try to init with frame on the root VC.
override init(frame: CGRect) { super.init(frame: frame) }
Can someone walk me through the best approach? I have something like below but I get a nil value right when I'm trying to add the UILabels to subview of the custom class.
class ViewTemplate: UIView {
var tempLabel: UILabel!
var humidityLabel: UILabel!
override init () {
tempLabel = UILabel()
tempLabel.frame = CGRectMake(halfScreenWidth - 130, 120, 260, 130)
tempLabel.textColor = UIColor.whiteColor()
tempLabel.backgroundColor = UIColor.clearColor()
// similar stuff for humidityLabel
super.init()
addSubview(tempLabel)
}
override init(frame: CGRect) { super.init(frame: frame) }
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If not quite sure where the nil is coming from. But most importantly, I'm looking for the best practice in doing this.
Thanks!
For creating the individual View:
Create new View file. Subclass of UIView. Be sure to also create the xib file when doing this.
Draw your items in storyboard (labels, etc.) and connect them to the swift file
Be certain that you connect them in storyboard by making the xib file a Custom Class of the swift file you just created.
Instantiate them inside the awakeFromNib() method. Be sure to set their default values for text if they are labels. Otherwise they will come up as nil when instantiated and your app will crash.
import UIKit
class MyView: UIView {
#IBOutlet weak var templabel: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
self.tempLabel.text = "95 degrees"
}
For loading into viewcontroller:
1a. Create a viewcontroller that implements UIScrollView.
1b. Place a UIScrollview in your storyboard and connect it to that viewcontroller
class MyWeatherViewController: UIViewController, UIScrollViewDelegate {
#IBOutlet var scrollView: UIScrollView!
2a. In ViewDidLoad, create instances of the UIView you are looking to create. Populate them individually.
2b. In ViewDidLoad, set the scrollview width to the width of a single View. Set the height to the height of the single View * the number of Views you wish to display
var view1: MyView = MyView()
var view2: MyView = MyView()
let viewArray[MyView!] = [view1, view2]
for items in viewArray {
var frame = scrollView.bounds
//Set the origin of the views
frame.origin.x = 0.0
frame.origin.y = frame.size.height * items
//create a view out of the object provided, define it's frame, and add to scrollview
let viewToAdd = viewArray[items].view
newPageView.frame = frame
scrollView.addSubview(viewToAdd)
//Set up your labels to be displayed. You must do this AFTER you load the load into the scrollview
viewArray[items].tempLabel.text = "105 degrees"
}
Keep in mind that you will want to set up another data model to hold the information that is to be displayed. I would recommend creating an array that holds the information you wish to display, so you can do viewArray[items].tempLabel.text = tempArray[items] with all the labels you wish to set.
This should align them vertically with the ability to scroll and see all items.
This will also let dynamically decide how many views to add based on cities the user has saved. Just modify the logic to read however many cities the user has etc.
You're on the right track.
Declare the properties for the labels. It looks like you've already done that. Personally, I would declare those as constants with let instead of var because you should always have those labels.
Override the init(frame: CGRect) method. Again, it looks like you've already started this. The reason you should override init(frame: CGRect) instead if init() is because init() just calls init(frame: CGRectZero). If you only implement init() then some of your properties may not get initialized (although this is way less of an issue with Swift since you can declare properties as constants so the compiler throws an error). In your initializer you're going to want to init all your labels & set them up right before you call super.init(frame). After the super.init(frame) method is called you should add the labels as subviews of self.
Override layoutSubviews() if you are laying your UI out programmatically. This method is where you will do all the sizing and laying out of your subviews. I do everything this way so it's what I'm most familiar with. If you're using AutoLayout I believe it's best practice to apply those constraints in the class's initializer since they should only ever need to be set up once.
Optionally, you may also want to override sizeThatFits(). This will ensure your view is always the proper size when sizeToFit() is called on it.
This goes not just for UIView but any subclass of it.
You can use a UIViewController extension and initialize a UIView function that takes a UIView. You can use this anywhere in your project.
extension UIViewController {
func setView(_ view: UIView){
view.backgroundColor = UIColor()
view.layer.cornerRadius = 5.0
// customize your view
}
}
Then: Declare you view in your Controller and pass it when the function is called on viewDidLoad()
let myView = UIView()
setView(myView)

Resources