Add UIView as SuperView subView from its own UView SubClass - ios

Actually I think this one is very simple, and I'm missing something.
I'm creating Subclass of UIView, and instead of adding it as a subView from the ViewController like this :
rateus = RateFrameWork(AppID: "asdasds", BackGroundColor: UIColor.blueColor())
self.addSubView(rateus)
I'm trying adding it from the subclass, this is what I tried so far :
class RateFrameWork: UIView
1) From the super init coder(dosent work)
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
superview?.addSubview(self)
}
2) From the init method(dosent work)
init?(AppID: String!, BackGroundColor: UIColor!)
super.init(frame: CGRect(x: 0 , y: 0, width: UIScreen.mainScreen().bounds.size.width , height:UIScreen.mainScreen().bounds.size.height))
superview?.addSubview(self)
Any suggestions? How can I add it as subview from its own subclass?

You can't do it because this is contradictory to the idea.
A view does not have superview unless it's to be added as subview, and a view does not know that it would be added as a subview under what circumstances and when.
Some special views(e.g. AlertView、ActionView) can be added to the keyWindow but your view not.

Related

What is the purpose of using super.init creating custom view with xib?

I want to understand why we use super.init all the time.
For example,
class HeaderStyle1: UIView {
var subview: UIView! //NİB
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit(){
subview = Bundle.main.loadNibNamed("HeaderStyle1", owner: self, options: nil)?.first as! UIView
subview.frame = bounds
subview.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(subview)
}
First: When I instantiate
header = HeaderStyle1(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 80))
super.init(frame: frame) instantiates UIView. Why we need this?
class ViewController: UIViewController {
var header: HeaderStyle1!
override func viewDidLoad() {
super.viewDidLoad()
createView() }
func createView() {
//Create Header
header = HeaderStyle1(frame: CGRect(x: 0, y: 0, width: self.view.fr
self.view.addSubview(header)
}
Second: I add header to UIViewController view as subview. Here header is an instance of HeaderStyle1. So I actually add a class to UIViewController view. How does UIViewController view show instance as view? Is it because of super.init or something?
Thank you
self.view.addSubview(header)
First , as every class is a subclass of another You call super.init is done to insure that if you call some Instance Variable / Method of the super class, you'll be able to do so as the super class is successfully initiated
suppose that i sublassed NSArray and in some point in app i want to call
NSArray*er = [myArr mutableCopy];
the copy process will fail if [super init]; failed when i subclassed NSArray
as mutableCopy is a method in NSObject that i ignored it's super init
Second , you actually adding an instance of a UIView (HeaderStyle1) to the current view controller's view (which also an instance of UIView) not class to class , this is a hierarchy made by Apple so developers can add different views to their apps to satisfy certain layout needs and modularize their Design so on for all UIKit components (UILabel,UITextfeild.....) that are pre-designed in IOS frameworks to display common components that make sense in small Mobile devices

Adding a UIView to the bottom of a UICollectionViewController

I have a UICollectionViewController embedded inside a UINavigationController which in turn embedded inside a UITabBarController.
I want to add a UIView to the UICollectionViewController just above the tab bar (shown by red rectangle).
I have the UIView created separately as a nib file.
import UIKit
class BottomView: UIView {
#IBOutlet var view: UIView!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
fileprivate func commonInit() {
Bundle.main.loadNibNamed("BottomView", owner: self, options: nil)
view.frame = self.frame
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(view)
}
}
And I initialize and add it in the UICollectionViewController like so.
class CollectionViewController: UICollectionViewController {
fileprivate var bottomView: BottomView!
override func viewDidLoad() {
super.viewDidLoad()
let yPos = view.bounds.height - (tabBarController!.tabBar.frame.size.height + 44)
bottomView = BottomView(frame: CGRect(x: 0, y: yPos, width: view.bounds.width, height: 44))
collectionView?.addSubview(bottomView)
}
// ...
}
I figured if I substract the combined height of the bottom view plus the tab bar from the entire view's height, I should be able to get the correct y position value. But it's not happening. The view is getting added but way off screen.
How do I calculate the correct y position without hardcoding it?
Example demo project
I would suggest adding the BottomView to the UICollectionViewController's view rather than to the collection view itself. This is part of the problem you're having.
You're also trying to set the frame of the BottomView in the viewDidLoad() method using values from view.bounds. The CGRect will return (0.0, 0.0, 0.0, 0.0) at this point because the layout has yet to take place, which is most likely why your positioning is off. Try moving your layout logic to the viewWillLayoutSubviews() method and see if that helps.
A better approach would be by setting auto layout constrains rather than defining a frame manually, this will take a lot of the leg work out for you.
Here's a quick example:
self.bottomView.translatesAutoresizingMaskIntoConstraints = false
self.view.insertSubview(self.bottomView, at: 0)
self.bottomView.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor).isActive = true
self.bottomView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.bottomView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
self.bottomView.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
You can apply autolayout logic in your viewDidLoad() and it should work correctly.
You can find some more information on setting autolayout constraints programatically here:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html
Sounds what you want to achieve is exactly the footer view for the UICollectionView.
A footerView is like a view that will stick to the bottom of the collectionView and wont move with the cells.
This will help you add a footer View: https://stackoverflow.com/a/26893334/3165112
Hope that helps!

UITextField border with AutoLayout is flickering

I am designing screen like a form, containing few UITextFields using AutoLayout. I wanted to set border only at bottom of the UITextFields. I have set border using CALayer. But UITextField occupies its height(after autolayout is applied to it) in method viewDidAppear, so adding border to UITextField in viewDidAppear makes it appear as if its flickering. So Is there any other way to set border to UITextFeild at bottom with AutoLayout.
class CustomTextField: UITextField {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
self.commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
func commonInit() {
self.borderStyle = .none //To remove default border.
let bottomBorder = UIView()
bottomBorder.frame.size = CGSize(width: self.frame.size.width, height: 1)
bottomBorder.frame.origin = CGPoint(x: 0, y: self.frame.size.height - 1)
bottomBorder.backgroundColor = UIColor.lightGray
bottomBorder.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
self.addSubview(bottomBorder)
}
}
Finally i achieved it by creating CustomUITextField with AutoLayout. Just apply above class to your UITextField in interface builder.
If I'm understanding you, all you have to do to solve your problem is to call the method "to draw the border" in the method above instead of viewDidAppear. I guess it can sove your problem.
override func viewDidLayoutSubviews() {
//A UIViewController's overrode method
//call you method here
}
This method is called before the view appears, and after the layout of the subviews. When you're working - changing I mean - layers, you should always use it instead of viewDidLoad, for example.
Hope it helps :)
Steps - Select the textfield -> show attribute inspector right panel and select dotted border style .
Next drag the uilabel into the scene and make it 1.0 and width what do you want, keep the bottom of the textfield. So your problem is solved. This may help you.
If you are using the simulator to test that. It looks like its flickering but its not.
If you are scaling the simulator to 25%. The 1px lines appears and
disappears when you scroll cause the screen resolution you have is
less than the real device resolution.
Test it while scaling the simulator to 100%. cmd+1

UIView subclass bounds incorrect in console

Here i have created UIView subclass named as CustomSlider. CustomSlider added on Storyboard and connected with storyboard outlet reference. When i am printing the bounds, I am having issues with printing the bounds, its providing incorrect console output.
class CustomSlider: UIView {
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
func setupView() {
print("self.bounds.height \(self.bounds)" )
self.backgroundColor = UIColor.red
}
}
class ViewController: UIViewController {
#IBOutlet weak var slider: CustomSlider?
override func viewDidLoad() {
super.viewDidLoad()
slider?.setupView()
}
}
When i am trying to build the output bounds displaying as below :
self.bounds.height (0.0, 0.0, 1000.0, 1000.0)
But i'm setting constraints trailing, top, right, height. I have tried below line also.
slider?.translatesAutoresizingMaskIntoConstraints = false
here is screenshot my project looks like
My expected console output would be the slider bounds.
Your call to print the bounds comes too soon. A view being loaded from a xib/storyboard has bogus bounds (as shown) until layout has occurred. Layout has not occurred at the time of viewDidLoad; the view is not yet in the interface and no sizes are real yet. Postpone your access to bounds until at least after viewDidLayoutSubviews has been called for the first time.

How to load xib to UIView subclass, Swift, iOS

I have a .xib file called ContentView that I want to use as the view for a class called ContentView, however I cannot seem to load it.
class ContentView: UIView {
override init() {
super.init(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
}
I'm aware that you can load a xib using the following method but this gives a error when I do so:
var contentViewXib: NSArray = NSBundle.mainBundle().loadNibNamed("ContentView", owner: nil, options: nil)
I have also set the xibs files owner to ContentView and set its Custom Class in interface builder to the class I want to use it with.
Any help is appreciated. Thanks
If you set File's owner to ContentView, then view in your XIB is not ContentView, it is normal UIView that ContentView can retrieve.
In the init method of ContentView, call NSBundle.mainBundle().loadNibNamed() just like you wrote in your question and then type:
self.addSubview(contentViewXib[0])
You will also need to set constraints or autoresizing mask for this new view.
EDIT:
Another solution is to select view directly (not File's Owner) and change its class to ContentView.

Resources