xcode - Error when attaching xib file to custom UIView class - xcode

I have a UIView class where I create some buttons and labels programmatically, and I have a view inside a certain ViewController that is attached to this class. The class works perfectly fine as expected, but whenever I try to link this custom UIView class to a xib view i get these errors:
-"Main.storyboard: error: IB Designables: Failed to update auto layout status: Interface Builder Cocoa Touch Tool crashed"
-"Rendering the view took longer than 200 ms. Your drawing code may suffer from slow performance.
"
-"error: IB Designables: Failed to render instance of MyView: Rendering the view took longer than 200 ms. Your drawing code may suffer from slow performance.
"
Here is the initialization code in the UIView class:
#IBDesignable class MyView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
func loadFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "MyView", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func setup() {
var view = loadFromNib()
view.frame = bounds
view.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
addSubview(view)
}
}
Both the class and the xib file have the same name,
UPDATE: I found out that the:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
Is the one responsible for all the errors, but if I deleted it's content and ran the app the view doesn't show as expected, I also tried awakeFromNib() and same issues happened.

Not quite sure what is causing your specific errors, but here is what helped me when I was having trouble connecting a UIView in a xib file to a custom UIView class:
1) Delete the contents of your DerivedData sub-folder:
(The contents of DerivedData will be recreated when you build your projects again.)
Your folder can be found by visiting the "Locations" tab in your XCode preferences.
Just delete the folders inside and re-build / run your project again.
2) Clean your project
Product -> Clean (Or just use the [command + shift + K] command.
3) Restart xcode and re-build / run your code.
Hope this helps!

Related

Cocoa Autolayout issue - NSTextView inside a custom NSView

I'm currently writing a Cocoa app, in Swift to exercise the language. I'm not too familiar with the AppKit framework yet, but now I bumped into an interesting problem.
It simply contains an NSWindow, and my custom NSView inside. With autolayout I control the size of the NSView, depending the resized window.
Base structure
As for my custom view, I'd like to have my NSView as a container, and I have an NSTextView inside it.
import Foundation
import AppKit
class Fucky2View: NSView
{
var textView : NSTextView!
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect)
{
super.init(frame: frame)
commonInit()
}
func commonInit()
{
print("initing")
self.wantsLayer = true
self.layer?.backgroundColor = NSColor.red.cgColor
textView = NSTextView.init(frame: self.bounds)
self.addSubview(textView)
self.translatesAutoresizingMaskIntoConstraints = false
textView.translatesAutoresizingMaskIntoConstraints = false
textView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
textView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
textView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
textView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
func displayText(_ text : String)
{
let attrStr : NSAttributedString = NSAttributedString(string: text+"\n")
textView.textStorage?.append(attrStr)
}
}
So I just want to keep the NSTextView as same size as the view itself. I colored the view background to red, to see if its filling properly, but this is what happens:
https://www.youtube.com/watch?v=LTQndoGzzEM
The textview's height is sometime set properly, but most of the times not.
I recreated the same exact app with UIKit, UIViewController, UIView and UITextView, tested on iPhone (I resized the screen with rotation), and this was working correctly.
Do anyone has any idea? I played along with priorities, but did not helped. Tried several things from NSView, concurrent draw, etc, did not helped.
Tried with NSLayoutConstraint instead of anchors, no change.
The only thing was if I set the TextView's frame in the custom NSView's drawRect: method, but I would like to do a nicer solution.
Or has anyone any other idea?
macOS Sierra 10.12.6, XCode 9.2
Thanks

Xcode memory graph debugger not showing cycles

I have been using the Xcode memory graph debugger to find cyclic references in our project and I've found a few of them.
However, I haven't been able to see the cycles in the graph. Only by inspecting the code.
For instance I'll see ...
ViewControllerA ---[parentViewController]---> ViewControllerB
But in code they are created like ...
class ViewControllerA: UIViewController {
let parentViewController: UIViewController
}
and...
class ViewControllerB: UIViewController {
let otherViewController: UIViewController!
viewDidLoad() {
...
otherViewController = ViewControllerA(parentViewController: self)
}
}
Clearly this is a cyclic reference. But it only shows one arrow in the graph.
Is there a way to make this show both arrows in the graph?
Just created an example...
New Project - Single view - Edit ViewController.swift to...
import UIKit
class ViewController: UIViewController {
var other: ViewControllerB!
override func viewDidLoad() {
super.viewDidLoad()
other = ViewControllerB(other: self)
}
}
class ViewControllerB: UIViewController {
let other: UIViewController
init(other: UIViewController) {
self.other = other
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In Memory Graph Debugger...
Focus on ViewController...
Focus on ViewControllerB...
From these I can infer that there is a reference cycle. But there are tutorials on the web where it actually shows the cycle with arrows following a cycle around the objects...
Like this from Use your loaf
Reference cycles only appear in the memory graph debugger for leaked objects. Because ViewController is referenced by UIWindow, it's not considered leaked, so the cycle between the view controllers isn't displayed.

Failed to render instance of ClassName: The agent threw an exception loading nib in bundle

When I include my custom IBDesignable view in a storyboard or another nib, the agent crashes and throws an exception because it can't load the nib.
error: IB Designables: Failed to update auto layout status: The agent raised a "NSInternalInconsistencyException" exception: Could not load NIB in bundle: 'NSBundle (loaded)' with name 'StripyView'
Here's the code I use to load the nib:
override init(frame: CGRect) {
super.init(frame: frame)
loadContentViewFromNib()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
loadContentViewFromNib()
}
func loadContentViewFromNib() {
let nib = UINib(nibName: String(StripyView), bundle: nil)
let views = nib.instantiateWithOwner(self, options: nil)
if let view = views.last as? UIView {
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
}
}
The view loads from the nib correctly when I run in the simulator, why won't it display in Interface Builder?
When Interface Builder renders your IBDesignable views, it uses a helper app to load everything. The upshot of this is that the mainBundle at design time is related to the helper app, and it's not your app's mainBundle. You can see that the path mentioned in the error has nothing to do with your app:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Overlays
When loading the nib, you're relying on the fact that passing bundle: nil defaults to your app's mainBundle at run time.
let nib = UINib(nibName: String(describing: StripyView.self), bundle: nil)
So instead, you need to pass the correct bundle here. Fix the above line with the following:
let bundle = Bundle(for: StripyView.self)
let nib = UINib(nibName: String(describing: StripyView.self), bundle: bundle)
That will make Interface Builder load your nib from the same bundle as your custom view class.
This applies to anything that's loaded from a bundle by your custom view. For example, localised strings, images, etc. If you're using these in your view, make sure you use the same approach and explicitly pass in the bundle for the custom view class.
The same viewpoint with "Josh Heald", We can't pass nil for bundle. And
this one for who in object - C:
- (UIView *) loadViewFromNib{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
UIView *v = [[nib instantiateWithOwner:self options:nil]objectAtIndex:0];
return v;
}

Storyboard UIView background color disappears when assigned #IBDesignable custom class

Steps to reproduce:
Create class like MyIcon below;
Drag a UIView onto storyboard (background is white);
Set UIView's custom class to MyIcon; and
Background color disappears.
MyIcon.swift:
import UIKit
#IBDesignable class MyIcon: UIView {}
See storyboard screenshot below for evidence of loss of white background color:
Probably it's a missing init function in the MyIcon class.
#IBDesignable needs at least these two functions:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
To make sure, you can also have a look at the crash reports in /Users/{yourusername}/Library/Logs/DiagnosticReports/

how to presentViewControllerAsSheet on OSX Mavericks?

It's a long story, but to cut it short; my first OSX app was written (on Yosemite) in Swift using a storyboard until I found out my (finished) app will not run on Mavericks. I need to run on Mavericks, so I have replaced the storyboard with NIBs.
My problem is with the segues; I was using 'sheet type' segues to show other view controllers in a sheet over the main view controller. A call to the presentViewControllerAsSheet method of NSViewController is a good replacement as it looks the same, but this API was introduced in Yosemite - so I need to work out how to do this for Mavericks.
In the action for a button on the main view, I've tried using beginSheet like this:
secondViewController = SecondViewController(nibName: "SecondViewController", bundle: nil)
self.view.window?.beginSheet(secondViewController!view.window!, completionHandler: nil)
But the second view controller's window is null at runtime. I've tried adding the new view controller as a subview to the application window but this is an unrecognised selector:
NSApplication.sharedApplication().windows[0].addSubView(secondViewController!.view)
I've search high and low for a description of how to show a sheet and all I can find is: Can a view controller own a sheet? but I'm sorry to admit I don't understand the answer. Can anybody help me with some working code? I'm beginning to worry that I'm trying to do something unusual but it looks OK on Yosemite, so how did people do this before Yosemite was released?
EDIT
I still haven't got to the solution, so I have put together a small app which shows the problems I'm having.
In AppDelegate.swift:
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var mainViewController: FirstView!
func applicationDidFinishLaunching(aNotification: NSNotification) {
mainViewController = FirstView(nibName:"FirstView", bundle: nil)
window.contentView = mainViewController.view
mainViewController.view.frame = (window.contentView as! NSView).bounds
}
}
In FirstView.swift (associated NIB has a 'open sheet' button)
class FirstView: NSViewController {
var secondView: SecondView?
var secondWindow: SecondWinCon?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func pressButton(sender: AnyObject) {
secondView = SecondView(nibName: "SecondView", bundle: nil)!
// method 1 - this is the behaviour I want (but it only works on OSX 10.10)
// presentViewControllerAsSheet(secondView!)
// method 2 - this just creates a floating window
// self.view.addSubview(secondView!.view)
// self.view.window?.beginSheet(secondView!.view.window!, completionHandler: nil)
// method 3 - this also creates a floating window
secondWindow = SecondWinCon(windowNibName: "SecondWinCon")
self.view.window?.beginSheet(secondWindow!.window!, completionHandler: nil)
}
}
In SecondView.swift (associated NIB has a 'close' button)
class SecondView: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func dismissPressed(sender: AnyObject) {
if (presentingViewController != nil) {
presentingViewController?.dismissViewController(self)
} else {
self.view.window?.sheetParent?.endSheet(self.view.window!)
}
}
}
In SecondWinCon.swift (Associated NIB is empty)
class SecondWinCon: NSWindowController {
var secondView: SecondView?
override func windowDidLoad() {
super.windowDidLoad()
secondView = SecondView(nibName: "SecondView", bundle: nil)!
self.window?.contentView.addSubview(secondView!.view)
}
}
If method 1 is uncommented, you will see the behaviour I'm trying to emulate (remember it only works on OS X 10.10). Method 2 or 3 displays the second view, but not as a sheet.
I have the same problem, and found maybe is't an issue related to view life cycle.
When I call presentViewControllerAsSheet in viewDidLoad, sheet will not shown, and you will get this in console:
Failed to set (contentViewController) user defined inspected property on (NSWindow): presentViewController:animator:: View '''s view is not in a window/view hierarchy.
If you trigger this in viewWillAppear or viewDidAppear, it's totally no problem.
UPDATE
Okay, let's make it clear.
For this initial storyboard, NSWindowController is connected with a view controller, think this as a root view controller (RootVC).
Create another view controller desired as a sheet in storyboard (SheetVC).
in viewWillAppear or viewDidAppear of RootVC, [self presentViewControllerAsSheet: SheetVC]
The sheet will show, no additional code required.
If you get here looking for a solution, I was nearly there with method 3. The important step I had missed was to turn off "Visible At Launch" in the NSWindowController's NIB (it's an attribute of the NSWindow). In my sample code, this was in SecondWinCon.nib.

Resources