I create two custom views using nib files and I create one instance for each class and then I added the two views to my main view , the first view is added and the second is loaded but not visible.
I load the views from the nib file using the following class method
class func instanceFromNib() -> CustomView {
let view = NSBundle.mainBundle().loadNibNamed("CustomView", owner: self, options: nil).first as! CustomView
return view
}
I need help to figure out what is going wrong with the second view.
try this:
view.translatesAutoresizingMaskIntoConstraints = true
view.autoresizingMask = .None
Related
I have one view controller which is a subclass of UITableViewcontroller, that UITableViewcontroller i need show under one UIView which I need to assign Corner Radius so it will match with my design.
UITableViewcontroller is the Generic tableview class which I have used in the whole project so I couldn't make changes in the structure.
All my ViewController are created programmatically, i have not used anywhere Storyboard.
Here is my Viewconroller Code where I am implementing
headerViewController is part which i mark as white
deal and team controller is my UITableviewController which i have added in SJSegment
private func setupSegmentController() {
segmentViewController.headerViewHeight = 350
segmentViewController.headerViewController = headerViewController
segmentViewController.segmentControllers = [
dealViewController,
teamViewController]
segmentViewController.delegate = self
addChild(segmentViewController)
segmentViewController.view.frame = view.bounds
view.addSubview(segmentViewController.view)
segmentViewController.didMove(toParent: self)
}
Below in red highlighted is the area which i need to design
What I understand you want to add a view controller as child in some other view controller. You have to use ContainerView in the View Controller where you want to show that table Table View Controller. Create an outlet of that Container View then add the table view as child.
let yourTableViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "yourTableViewController") as! UITableViewcontroller
yourTableViewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
yourTableViewController.view.frame = self.containerView.bounds
self.addChild(yourTableViewController)
containerView.addSubview(yourTableViewController.view)
yourTableViewController.didMove(toParent: self)
You need to subclass UITableView and view.addSubview(yourCustomClass) . It is not necessary to add UITableViewController inside your UIView
Controllers it's also a basic class that can control your view. You can add as subview tableView you your generic view and set their delegate and dataSource to the main view controller. You don't need to create tablewviewcontroller for this.
all you need is - (inside main view controller)
tableView.delegate = self
tableView.dataSource = self.
and after this you must implement protocol conformance
MainViewController: UITableViewCOntrollerDelegate {
// implementation here
}
MainViewController: UITableViewControllerDataSource {
// implementation here
}
After this you can customise your table view like view and do what you want
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
guard let view = loadViewFromNib() else { return }
view.frame = self.bounds
self.addSubview(view) // My question
}
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: self.className, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? UIView
}
i want customXibView initial with nib .look like self = loadViewFromNib(), not by self.addSubview(nibView) .
I found that many of the answers are all done self.addSubview(nibView). In fact, look our view hierarchy,we will found a extra view.
This is my view hierarchy
Is there any way to instance xib to storyboard, not by self.addSubview(nibView).
if you don't understand my question,look this Custom UIView from Xib - requirements, good practices. I hope some one help me.
There are three scenarios:
You have CustomView class with all of its UI defined
programmatically in its drawRect method.
You have CustomView class with its UI defined in a subview of storyboard scene with its class set to 'CustomView'.
You have CustomView class with its UI defined in a separate xib.
Now if you want to include your CustomView in as a subview in your viewcontroller, the method differs depending how your class is defined in the first instance. Secondly how do you want to include it in you viewcontroller.
Adding CustomView with scenario 1:
Initialise the CustomView instance in your viewDidLoad method and add it to your viewcontroller's view as a subview.
Adding CustomView with condition 2:
You don't have to do anything in this scenario. Storyboards will initizlize your class for you. You can do additional setup in init(coder) method that will be called when you view is initialized.
Adding CustomView with condition 3:
The proper way to initialise a view with an xib is to initialise it programmatically via UINib's inistantiateWithOwner method. Then add the view as a subview in the viewDidLoad method of your viewcontroller.
What you are doing (or what I can guess seeing your image hierarchy) is that you have changed the class of your UIView to your custom class. Now since you have a separate xib, you are initialising it in init(coder) method and adding it as a subview to the view that was defined in and initialised by the storyboard. Hence you end up with an extra container view.
If you don't want the extra view, add your custom view programmatically.
OR
(If possible) copy the view defined in xib and place it in the viewcontroller dock and then link the view with an outlet and add it in viewDidLoad method.
Here's the answer you've wanted all along. You can just create your CustomView class, have the master instance of it in a xib with all the subviews and outlets. Then you can apply that class to any instances in your storyboards or other xibs.
No need to fiddle with File's Owner, or connect outlets to a proxy or modify the xib in a peculiar way, or add an instance of your custom view as a subview of itself (i.e. no need for self.addSubview(view), which is your question).
Just do this:
Import BFWControls framework
Change your superclass from UIView to NibView (or from UITableViewCell to NibTableViewCell)
That's it!
It even works with IBDesignable to refer your custom view (including the subviews from the xib) at design time in the storyboard.
You can read more about it here:
https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
And you can get the open source BFWControls framework here:
https://github.com/BareFeetWare/BFWControls
And here's a simple extract of the NibReplaceable code that drives it, in case you're curious:
https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4
Tom 👣
Its not necessary that you have to add the xib view as subview. Think about the usage, as per my understanding there are tow scenarios
Suppose you have a view called customXibView class and its xib, now in the xib select the view and and from the right side panel select [identity inspector] option and check, under [custom class] by default its class is UIView. So you can keep it as UIView and set the file owner as customXibView and load it from xib as UIView instance as you are doing now. So, now the question is if you allocate customXibView object manually or in some other xib how it will show your custom components which are in your view xib. So in that case in your customXibView's init method you have to load the UIView xib and add it to customXibViewobject's view.
Another case where you can set the xib's view [custom class] as your class customXibView in this case when you call the below method
func loadViewFromNib() -> customXibView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: self.className, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as? customXibView
}
You directly get an object of customXibView with UI loaded no need to add it to any other view. But in this case if you allocate the customXibView class object your UI will not be loaded if you don't do what what you are doing now. If any doubt plz comment.
I have created on custom View which contain one label and button
I have created outlet for both in UIView class
I have one UIViewController in which i have subview that custom view
now i want to change label name and want to do something on button click.
let commonView = UINib(nibName: "CommonView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! CommonView
firstView.addSubview(commonView)
commonView.lblCommon.text = "We can change label in particular Screen"
I am able to change label text but how can i get button action event in UIViewcontroller?
Use protocol
In your customView declare a protocol
protocol CustomViewProtocol : NSObjectProtocol{
func buttonTapped()
}
Now create a variable in custom view
weak var delegate : CustomViewProtocol? = nil
In your viewController confirm to protocol
extension ViewController : CustomViewProtocol {
func buttonTapped() {
//do whatever u waana do here
}
}
set self as delegate in view controller
let commonView = UINib(nibName: "CommonView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! CommonView
firstView.addSubview(commonView)
commonView.delegate = self
finally in IBAction of button in custom view trigger delegate
self.delegate?.buttonTapped()
In Swift 4:
If you want to catch the tap within your UIViewController do this in the viewDidLoad() after you instantiate your view from a nib (eg called aView):
let tap = UITapGestureRecognizer.init(target: self, action: #selector(tapAction))
aView.addGestureRecognizer(tap)
Below viewDidLoad declare the following function and do whatever you want within its body:
#objc func tapAction() {
//Some action here
}
(A note: In Swift 4 the #objc annotation is obligatory, because it denotes that you want the function to be visible to the Obj-C runtime, which in UIKit handles (among other things) user interactions through its messaging system.)
Since this UIView instance belongs in the view hierarchy of this UIViewController it makes sense to handle the taps within this UIViewController. This gives you the ability to reuse this UIView within any other UIViewController and handle its tap logic from the respective UIViewController.
In case there is a particular reason you want the taps handled within the UIView, you can use the delegate pattern, a closure, or in a more advanced implementation a Reactive framework (ReactiveSwift or RxSwift/RxCocoa)
The way to do it with RxSwift/RxCocoa:
view.rx
.tapGesture()
.when(.recognized)
.subscribe(onNext: { _ in
//react to taps
})
.disposed(by: stepBag)
more ways to manage Gestures, can be found at the RxSwift github page: https://github.com/RxSwiftCommunity/RxGesture
I'm creating a custom presentation controller for dimming the background when a view controller is presented. The presentation controller adds a couple of subviews when the transition begins which works great.
However, I would like to setup the chrome (the presentation "frame") in Interface Builder because that way it's easier to layout. Thus, I created a XIB file for designing the chrome. It includes a semi-transparent background view and a ❌-button in the upper left corner to dismiss the presented view controller. For these subviews I need outlets and actions in my presentation controller (which is not a UIViewController subclass).
In order to achieve that I set the XIB's file's owner to my custom presentation controller, both in Interface Builder and in code when instantiating the view:
lazy var dimmingView = Bundle.main.loadNibNamed("PresentationChromeView",
owner: self,
options: nil)?.first
as! UIView
I then created the respective outlets and actions by CTRL+dragging to my presentation controller:
#IBOutlet var closeButton: UIButton!
#IBAction func closeButtonTapped(_ sender: Any) {
presentingViewController.dismiss(animated: true, completion: nil)
}
However, at run-time the app crashes because UIKit cannot find the outlet keys and when removing the outlets the actions methods are not triggered. So in neither case is the connection established.
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<_SwiftValue 0x600000458810> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key closeButton.'
The only reason I can think of why this doesn't work would be that it's not allowed to create outlets and actions with classes that don't inherit either from UIView or UIViewController.
Is that assumption correct?
Is there a way to create outlets and actions with non-view(-controller) classes?
You can create IBOutlets in any class inheriting from NSObject. The issue here seems to be that you didn't set your custom class in interface builder:
'[<NSObject 0x60800001a940> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key closeButton.'
While decoding your Nib, NSCoder attempts to set the closeButton property on an instance of NSObject, which of course doesn't have this property. You either didn't specify your custom class or made an invalid connection.
OK... the main problem is that the XIB / NIB file has to be instantiated, not just load the first item.
All these changes are in DimmingPresentationController.swift:
// don't load it here...
//lazy var dimmingView = Bundle.main.loadNibNamed("DimmedPresentationView", owner: self, options: nil)?.first as! UIView
var dimmingView: UIView!
then...
private func addAndSetupSubviews() {
guard let presentedView = presentedView else {
return
}
// Create a UINib object for a nib (in the main bundle)
let nib = UINib(nibName: "DimmedPresentationView", bundle: nil)
// Instante the objects in the UINib
let topLevelObjects = nib.instantiate(withOwner: self, options: nil)
// Use the top level objects directly...
guard let v = topLevelObjects[0] as? UIView else {
return
}
dimmingView = v
// add the dimming view - to the presentedView - below any of the presentedView's subviews
presentedView.insertSubview(dimmingView, at: 0)
dimmingView.alpha = 0
dimmingView.frame = presentingViewController.view.bounds
}
That should do it. I can add a branch to your GitHub repo if it doesn't work for you (pretty sure I didn't make any other changes).
The issue that caused the app to crash was that the dimmingView's type could not be inferred (which is strange because the compiler doesn't complain). Adding the explicit type annotation to the property's declaration fixed the crash. So I simply replaced this line
lazy var dimmingView = Bundle.main.loadNibNamed("PresentationChromeView",
owner: self,
options: nil)?.first
as! UIView
with that line:
lazy var dimmingView: UIView = Bundle.main.loadNibNamed("PresentationChromeView",
owner: self,
options: nil)?.first
as! UIView
The outlets and actions are now properly connected.
Why this type inference didn't work or why exactly this fixes the issue is still a mystery to me and I'm still open for explanations. 🙂❓
I'm defining a Custom UIView based on a XIB. Therefor I have copied CustomXIBSwift-master from Github. That works very well. I adapted it briefly for my purposes.
The label (lblTitle) is linked as a referencing outlet to the File's Owner
Now I also want to make a referencing outlet for the second sublabel to File's Owner. However I'm not able to linke this using the dot-line method nor by including the IBOutlet manually.
Based on the CustomXIBSwift-master the XIB is of type UIView. When I specify a custom class (instead of UIView) it crashes.
Part of the SWIFT code:
func loadViewFromNib() {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "SlidingAlertView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.addSubview(view);
}
How can I make a referencing outlet from this View to the Swift file?
I was not able to make a connection between the UILabels and the File's Owner. It appeared that also for the File's Owner thee Custom Class can be specified.