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()
}
}
Related
When the app run in Simulator it works but in Storyboard can’t see the preview, why?
Swift code:
Simulator and Storyboard:
Can’t see the custom view background color and when drag UIButton object into custom view doesn’t see the real position(x, y) and its background color.
In Android Studio when you add an object (custom view) in layout.xml file you can see the preview automatically, is it possible to do the same thing in Xcode?
TL;DR; Call in prepareForInterfaceBuilder
// Call The Custom Setup Here
override func prepareForInterfaceBuilder() {
setupView()
}
Calling in layoutSubviews also works, but is called multiples times in runtime, prepareForInterfaceBuilder is called only for Designables Changes, and only with this purpose.
Long Code:
#IBDesignable
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
override func prepareForInterfaceBuilder() {
setupView()
}
func InitChildPosition() {
var i = 1
for _view in self.subviews {
if _view is UIButton {
_view.center.x = (_view.bounds.width / 2)
_view.center.y = (_view.bounds.height / 2)
}
if _view is UIButton && i == 2 {
_view.center.x = self.bounds.width - (_view.bounds.width / 2)
_view.center.y = self.bounds.height - (_view.bounds.height / 2)
_view.backgroundColor = UIColor.darkGray
}
i += 1
}
}
func setupView() {
self.backgroundColor = UIColor.black
InitChildPosition()
}
}
Call your CustomSetup() from init(frame:)
EDIT
Interface Builder uses init(frame:) to create and position views in storyboards.
You are modifying the subviews of CustomView in CustomSetup() which is called from the initializer. Your view does not have any button subviews in that case, because subviews can only be added after the view has been initialized.
You need to defer calling CustomSetup() to a later point, awakeFromNib() should work in your case.
Better yet, remove CustomSetup() completely and just style your UIButtons in the Interface Builder. You will get the same results without all the complexity.
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.
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
}
}
I have created a custom UIView in MySample.xib. I have added the class MyView to the File Owner of xib.
MyView.swift
class MyView: UIView {
#IBOutlet var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func setup() {
NSBundle.mainBundle().loadNibNamed("MySample", owner: self, options: nil)
self.addSubview(self.view)
}
}
I am now loading this MyView from MyController file like this:
MyController.swift
class MyController: UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
view.addSubview(MyView())
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Now to display this view, I am using to following code from another controller's UIButton:
presentViewController(MyController(), animated: true, completion: nil)
This does display the view on screen. But the problem is, it doesn't accept any user interaction. In my custom view, I have a UITableView which does display the data but it doesn't scroll or get tapped due to lack of user interaction.
Any idea what I am doing wrong?
There are some unnecessary things in your example.
I am still not sure what are you trying to do, but if you want to add a custom view from xib to your view controller then:
Create a view in a xib file , you don't need to override init , and you can't init view from xib using the default init UIView() , so please remove init method from your MyView class.
In your xib make sure that your view that you see in the IB is of the class type you want to use (i guess MyView class).
In your view controller init the view like this:
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Get all views in the xib
let allViewsInXibArray = NSBundle.mainBundle().loadNibNamed("MySample", owner: self, options: nil)
//If you only have one view in the xib and you set it's class to MyView class
let myView = allViewsInXibArray.first as! MyView
//Set wanted position and size (frame)
myView.frame = self.view.bounds
//Add the view
self.view.addSubview(myView)
//TODO: set wanted constraints.
}
}
You don't have to re-instantiate this twice
already if you using the design pattern.
It's so simple. Just write:
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Get all views in the xib
let view = MyView()
self.view.addSubview(myView)
//TODO: set wanted constraints.
}}
And It will work.
Instead of linking xib File's Owner class to MyView, I have to change the class of root view in xib to MyView. Then based on #Oleg Sherman code, it works perfectly with small changes of adding MyView() as owner to get all it's events, otherwise it will throw an error this class is not key value coding-compliant for the key ****.:
let allViewsInXibArray = NSBundle.mainBundle().loadNibNamed("MySample", owner: MyView(), options: nil)
Using File's Owner class to MyView is only required when you have to use the xib in Storyboard.
Not sure if there is a workaround to use File's Owner class to MyView when programmatically loading xib from custom controller like in my original question.
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)