Changing variables of a custom UIView in another custom ViewController - ios

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.

Related

Add subview in interfaceBuilder

I am trying to add a label to UIView and I would like to preview it in InterfaceBuilder, however it doesn't seem to work. not only I don't see label but It also doesn't append one to view hierarchy
#IBDesignable
class UIFloatingLabelInput: UIView {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
let Label : UILabel?
func AddLabel(){
Label?.text = "Hello World"
Label?.center = CGPoint(x: self.center.x, y: self.center.y)
self.addSubview(Label!)
}
override init(frame: CGRect){
//Init vars
Label = UILabel(frame: frame)
//Call Initializer
super.init(frame: frame)
AddLabel()
}
required init?(coder: NSCoder){
//Init vars
Label = UILabel(coder: coder)
//CallInitializer
super.init(coder: coder)
AddLabel()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
AddLabel()
}
}
but it doesn't work in IB, but works fine on a device.
You need to give your label frame to make it work in IB
required init?(coder: NSCoder){
//Init vars
Label = UILabel(coder: coder)
//CallInitializer
super.init(coder: coder)
//Give your label a frame
Label.frame = bounds
AddLabel()
}

Swift scrollview adding from XIB not scrollable at all

I'm trying to import from Xib full screen ScrollView into my ViewController.
Following that guide i've made many working examples of it, but when importing it from Xib, ScrollView not responding on scroll (not even bouncing)
My Xib View class:
class TestScroll: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.translatesAutoresizingMaskIntoConstraints = false
}
public static func getViewFromNib() -> TestScroll {
return UINib(nibName: "TestScroll", bundle: .main).instantiate(withOwner: nil, options: nil).first as! TestScroll
}
}
And this is how i adding it in ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let testScroll = TestScroll.getViewFromNib()
self.view.addSubview(testScroll)
}
Please help, i've checked many guides already, but not found working example with Xib.
You need to set a frame / constraints
let testScroll = TestScroll.getViewFromNib()
testScroll.frame = self.view.bounds
self.view.addSubview(testScroll)
leave this
self.translatesAutoresizingMaskIntoConstraints = false
only if you'll set constraints

Subclassed view's uiimage not appearing as IBDesignable

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!

Auto resize the UIView based on UILabel's content through Auto Layout

I have a requirement on showing a banner view to show a message,
Now the message's content may vary and the view should also resize depending on UILabels content.
UILabel is set to Word Wrap and numberOfLines is set to 0
The design in xib is,
And the respective class file is,
import UIKit
class ORABannerView: UIView {
#IBOutlet weak var bannerText: UILabel!
static func instantiate(message: String) -> ORABannerView {
let view: ORABannerView = initFromNib() ?? ORABannerView()
view.bannerText.text = message
return view
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.draw(rect)
}
}
The initFromNib is an implemented as UIView's extension,
extension UIView {
class func initFromNib<T: UIView>() -> T? {
guard let view = Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as? T else {
return nil
}
return view
}
}
Tried with layoutIfNeeded() on the view, but it's not working for me.
Any suggestions will be appreciated.
You may want to try the following code,
Create a method sizeHeaderToFit and which returns height of the UIView based on its content's height.
private func sizeHeaderToFit(headerView: UIView?) -> CGFloat {
guard let headerView = headerView else {
return 0.0
}
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
return height
}
Then you can call the above method from the respective code,
sizeHeaderToFit(headerView: yourView)
In ORABannerView class, add override layoutSubviews,
override func layoutSubviews() {
super.layoutSubviews()
bannerText.preferredMaxLayoutWidth = bannerText.bounds.width
}
And in xib file, keep the constraints to its superview rather than safe area for code optimisation. [Optional]
Hope it helps.
Remove trailing OR leading constraints
set the size to fit property programmatically
I expect the class to look like
class ORABannerView: UIView {
#IBOutlet weak var bannerText: UILabel!
static func instantiate(message: String) -> ORABannerView {
let view: ORABannerView = initFromNib()
view.bannerText.text = message
return view
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
super.draw(rect)
}
}
let v = ORABannerView.instantiate(message: "djhhejjhsdjhdjhjhdshdsjhdshjdhjdhjdhjddhjhjdshjdhdjshjshjdshjdshjdshjdshjdsjhdshjds")
self.view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: self.view.topAnchor,constant:50),
v.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant:0),
v.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,constant:0)
])
New users autolayout, always forgot about two useful constraints:
func setContentCompressionResistancePriority(UILayoutPriority, for: NSLayoutConstraint.Axis)
func setContentHuggingPriority(UILayoutPriority, for: NSLayoutConstraint.Axis)
It also exists in IB too:
Try to play with the priority parameter

Transparent view is black?

I'm trying to create a view that is transparent in certain spots to see an image behind. However, for some reason in the transparent part of the view, I'm seeing black, instead of what's behind the view. I've trimmed it down to very little code and don't understand why my transparent view shows black instead of red (the color of the view behind). Here's my code:
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let redView = UIView(frame: view.frame)
redView.backgroundColor = UIColor.red
let transparentView = TransparentView(frame: view.frame)
view.addSubview(redView)
view.addSubview(transparentView)
}
}
class TransparentView: UIView {
override func draw(_ rect: CGRect) {
UIColor.clear.setFill()
UIRectFill(rect)
}
I would expect the screen to be full red, but instead it shows full black. Before someone says it's a lot easier to make a clear view, I'm actually trying to do more complex things in drawRect, just dropped down to the most basic thing to try to debug my problem. What am I missing here?
Use self.isOpaque = false; to make the view/layer transparent even when drawRect is overriden.
class TransparentView: UIView {
override init(frame: CGRect) {
super.init(frame: frame);
self.isOpaque = false; //Use this..
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
UIColor.clear.setFill()
UIRectFill(rect)
}
}
I figured it out. Apparently even if you override draw, backgroundColor seems to still be considered, and defaults to black. I added the following to my transparent view class:
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
"drawRect: Implement this method if your view draws custom content. If your view does not do any custom drawing, avoid overriding this method." Link
That been said would be better as you said just set background color on Init.
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Personally i wont subclass a view for so little customization. Just set it while creating it. Also view setup is better on viewDidLoad, not in viewWillAppear. Since it will execute every time your view will go foreground and u will end with two transparent views added.
Also keeping those line in a extension with a private function helps to keep your code clear.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
}
//MARK: - Private Methods
extension ViewController{
fileprivate func setupViews(){
let redView = UIView(frame: view.frame)
redView.backgroundColor = UIColor.red
view.addSubview(redView)
let transparentView = UIView(frame: view.frame)
transparentView.backgroundColor = UIColor.clear
view.addSubview(transparentView)
}
}
Please notice that a more clear approach would be to create those Views in the Storyboard (not by code). Keep code clear and its easier to understand and see whats going on.

Resources