I am trying to present a view controller in a Forms dependency service. But I keep getting a black screen. I can get it to work when I use Swift in Xcode, but not using Xamarin.
Controller outlets are not loading either. Strange thing is changes to vc.View do appear. It’s as though it’s creating a blank view and not actually loading from the storyboard.
My code is as follows:
var storyboard = UIStoryboard.FromName("StoryboardView", null);
var controller = storyboard.InstantiateViewController("StoryboardViewVC");
var window = UIApplication.SharedApplication.KeyWindow;
var vc = window.RootViewController;
while (vc.PresentedViewController != null)
{
vc = vc.PresentedViewController;
}
vc.PresentViewController(controller, true, null);
Adding a call to LoadViewIfNeeded() does not fix the issue.
I have double-checked that the storyboard and controller names match, which they do. I’ve searched for answers, but everything I’m finding revolves around not writing code to load the controller from the storyboard or nib.
var controller = (UIViewController)storyboard.InstantiateViewController("StoryboardViewVC");
use class you assigned to your view that may load your view.
Related
Just imagine( as case) that i have TabBarController as root and several ViewControllers that are linked to this TabBarController( via Storyboard(!) that is important step,i mean programmatically it's something other).
And its works without any code behind on TabBarController class!
So i'm interested in right approach, how can i Inistiate Viewcontrollers inside TabBarController on right way!?
Or it's isn't necessary and OS will do it for me , because at current moment, i can do something like this :
public override void ViewDidLoad()
{ //No Init for VC that are inside linked to TabBAr
base.ViewDidLoad();
//Find all viewcontrollers that are linked in storyboard
foreach (var Item in ViewControllers)
{
Item.TabBarItem.Image =
UIImage.FromBundle("RandomImg");
//Change title for all VC to "Main"
Item.TabBarItem.Title = "Main";
break;
}
}
What if i will do initialization of ViewControllers inside TabBarController constructor.
What will be happened on behind the scenes? How the system will recognize my init of VC,or it's doesn't matter and thats will be an redundant action, because technique via storyboard anyway creates ViewControllers without any initialization on code behind!?
How to handle/customize correctly ViewControllers that are inside of TabBarController?(e.g. select another VC at beginning, change buttons on TabBar and etc)
I would like to take all answers on all my questions and if it possible, need an live example/article that explains more advanced about this stuff. Thanks!
I am working on a iOS app that displays lots of different files to the user. From video and audio files to html and Office/iWorks files. I have subclassed QLPreviewController, and want to use it to display some of these files. I create the object, pass it the file's url, set the ViewController's view's frame to replace the webView's frame in the parent viewcontroller. :
else if (QuickLookViewController.canOpenFile(previewItem: contentUrl as NSURL)) {
hideControls()
quickLook.contentURLs = [contentUrl as NSURL]
//add the QuickLookController's view to content display using webview's frame
self.view.addSubview(quickLook.view)
quickLook.view.frame = webview!.frame
quickLook.reloadData()
Using the view debug hierarchy tool, it shows the view set correctly, as well as setting quickLook.backgroundColor = UIColor.black just to test.
So I thought that maybe there was a problem with accessing the download and stored file, so in the viewDidLoad of QuickLookController I added some tests to ensure that the file is there:
super.viewDidLoad()
print("\nContent URL: \(contentURLs[0])")
print("\nContent URL Path: \(contentURLs[0].path!)")
self.delegate = self
self.dataSource = self
var error : NSError?
print("\nis reachable: \(contentURLs[0].checkResourceIsReachableAndReturnError(&error))")
if (error != nil) {
print("is reachable error -> \(error.debugDescription)")
}
print("\ndoes exist: \(FileManager.default.fileExists(atPath: contentURLs[0].path!))")
print("\nCan open: \(QuickLookViewController.canOpenFile(previewItem: contentURLs[0]))")
and the log statements come out as I expect:
Content URL: file:///var/mobile/Containers/Data/Application/B9D5C288-F889-4513-941E-2564F1C12F02/Documents/588c5a1e-dffe-47a8-9824-bc19463aafc2/d88a8dd5-40d1-4fdb-adf3-10fce1f6bf1f/fd73c162-5ac3-4269-8573-9c0b61bef7a7/fd73c162-5ac3-4269-8573-9c0b61bef7a7.pages
Content URL Path: /var/mobile/Containers/Data/Application/B9D5C288-F889-4513-941E-2564F1C12F02/Documents/588c5a1e-dffe-47a8-9824-bc19463aafc2/d88a8dd5-40d1-4fdb-adf3-10fce1f6bf1f/fd73c162-5ac3-4269-8573-9c0b61bef7a7/fd73c162-5ac3-4269-8573-9c0b61bef7a7.pages
is reachable: true
does exist: true
Can open: true
I even used a breakpoint in the the viewDidLoad to check that the quickLook's superview is set using 'po self.view.superview!.frame' as a llbd statement, and again received the output I expected.
I used this same class for another view stack in the app and it displays the files that are clicked, so it isn't making much sense to me. The only difference between the two uses, the second I am presenting the quickLook's view in a viewController that is presented modally.
ok I wanted to post the answer to the problem I was having. The issue was I was not adding the quickLook view controller to the ContentDisplayViewController with a parent-child relationship. So the updated code to add the view I wanted looks like:
//create the parent-child relationship for the view controllers
self.addChildViewController(quickLook)
//give the view it's frame
quickLook.view.frame = webview!.frame
//add the view and call the child's didMove() method.
self.view.addSubview(quickLook.view)
quickLook.didMove(toParentViewController: self)
This question gave me the answer:
Add child view controller to current view controller
Gave me me some reading material to read into, thanks for the post #Piyush Patel
I have a UIViewController that I have had in a storyboard for a while with no problems. As my application grew, and I was using that view controller in more and more places, I realized that I should probably make it more portable, rather than have so many segues to it from hither and yon across the board. I've done splits like this before, so I did what I figured was logical here. I selected that view controller, cut it, and pasted into an empty .xib file. After changing each call to performSegueWithIdentifier to an init(nibName:bundle:) and presentViewController, I get a crash, with an object found unexpectedly nil in viewDidLoad()...
I set the value of this object after each init(...) call, just before presenting the view controller. The nil object is called from viewDidLoad(). This is a problem. I just set this, and now it's gone?!
I overrode the init(...) method, and found that self in init(nibName:bundle:) doesn't have the same memory address as self in viewDidLoad(). Also strange.
I overrode the other init() methods, and found that, after I call to present my view, my object is being instantiated again via init(coder:)! The self in here happens to be the exact self where my property is found nil!
The only reason I see for init(coder:) to be called at all is that I am loading my view from a .xib, but I thought this was handled in init(nibNamed:bundle:)? According to the docs, I do indeed get a call to init(coder:) if I'm loading from a storyboard, and doesn't touch the former... It also says that the nib isn't loaded until the controller's view is queried. If I understand it correctly, my view shouldn't get queried until I present the view. As the crash happens only when I present it, the issue likely stems from that.
I'm stuck here. I still need to get this contextual information to the view controller before it's presented. I've even tried making a proxy class to do the instantiating and property setting before presentation, but I still can't shake this second instance! I get one from init(nibName:bundle:), and another from init(coder:). Neither gets presented, and the latter gives me a nil object error. Any help at all in understanding why this is, and how I might work around this bug (feature?) would be much appreciated. Thank you!
Update:
On a whim, I decided to paste the view controller back into the storyboard, separate from the main hierarchy, and try instantiating it by its identifier. It worked! Not entirely sure how, but by George it worked! Now my question is this: Why?? What is so terribly evil and taboo about .xibs that Xcode and iOS won't tell me? I'm not a little flummoxed by this behavior. I'll keep trying with the .xib, if only to keep Xcode from yelling at me about entrance points...
I don't know what dark magic Xcode is doing, but here's two helper methods I wrote to easily instantiate any Storyboard VC - you just need the Storyboard name and VC identifier (optionally, otherwise will initial VC). By splitting up my VCs into many different Storyboards, I avoid dealing with xibs while still keeping things simple. One loads it into a nav controller of your choice, the other just returns it by itself:
struct StoryboardHelper {
///instantiates a VC with (optional) identifier viewController from storyboardName, pushes it to hierarcy of navigationController, and runs setup block on it, animated specifies whether the push is animated
internal static func showStoryboard(storyboardName: String, viewController: String?, navigationController: UINavigationController, animated: Bool = true, setup: (UIViewController) -> () ){
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
let destinationVC = viewController != nil ? storyboard.instantiateViewControllerWithIdentifier(viewController!) : storyboard.instantiateInitialViewController()!
setup(destinationVC)
navigationController.pushViewController(destinationVC, animated: animated)
}
///instantiates and returns a VC with (optional) identifier viewController from storyboardName
internal static func instantiateViewControllerFromStoryboard(storyboardName: String, viewController: String?) -> UIViewController{
let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
return viewController != nil ? storyboard.instantiateViewControllerWithIdentifier(viewController!) : storyboard.instantiateInitialViewController()!
}
}
In my application, I have a mainViewController with some content on it. At some points, I load an overlay view controller from storyboard. The overlay view controller is smaller than the screen and is presented on top of the mainViewController. I initialize it the following way:
class MyOverlayViewController {
#IBOutlet var textLabel: UILabel!
#IBOutlet var countLabel: UILabel!
static let storyboard = UIStoryboard(name: "...", bundle: nil)
// Return a new view controller
class func newViewControllerWithData(data: AnyObject) -> UIViewController {
let vc = storyboard.instantiateViewControllerWithIdentifier("MyOverlayViewController") as! MyOverlayViewController
Timing.performAfterDelay(0) {
vc.titleLabel.text = data[...] // Load title label text
vc.countLabel.text = data[...] // Load count label text
}
return vc
}
}
I cannot set the text of the labels immediately in the method newViewControllerWithData, because that produces the following error: fatal error: unexpectedly found nil while unwrapping an Optional value. So the labels are nil when accessing them immediately in this method.
It seems like the two label outlets are not loaded immediately when the view controller is instantiated from storyboard, because this takes a (very) short time.
Therefore, I use my method Timing.performAfterDelay(0) which executes the code after the next run-loop cycle (it starts a timer with duration 0 and executes the code as callback). The code is (I have checked that) executed on the main thread.
The problem is the following:
Sometimes (not always, and not reproducible!), when loading the overlay view controller, for a fraction of a second the labels are empty (like I have defined them in storyboard) before they are showing the text.
So the user sees empty labels for a short moment before the actual data is loaded into the labels.
How can I fix this behavior?
Is it possible somehow to access the outlets immediately after instantiating the view controller from storyboard, without using Timing.performAfterDelay(0)?
Help would be appreciated.
Outlets are set after view is loaded i.e. when viewDidLoad gets called on the view controller. However, calling it directly like vc.viewDidLoad() will not work, you have to access the view controller's view like let dummyVariable = vc.view instead. Here's the code that force loads the view and then sets the label values.
class func newViewControllerWithData(data: AnyObject) -> UIViewController {
let vc = storyboard.instantiateViewControllerWithIdentifier("MyOverlayViewController") as! MyOverlayViewController
let _ = vc.view // force load the view
// now set your outlets as you please
vc.titleLabel.text = data[...] // Load title label text
vc.countLabel.text = data[...] // Load count label text
return vc
}
NOTE: This is not really a good practice though. MyOverlayViewController should be responsible for setting its label values instead of these being set from the outside. You could pass it the required data via a property or argument to a method, etc.
I want to add Layer SDK to my application (using Swift).
All view controllers here are created programmatically. Therefore I can't segue to them. I have 4 tabs in my application (UITabBarController). One of them is chat. In the chat tab I created a segue to UINavigationController. Now I want to load conversationListViewController in this UINavigationController. For that I created a class for this UINavigationController i.e. ConversationListViewController and added the following code:
class ChatNavigationViewController: UINavigationController {
var conversationListViewController: ConversationListViewController!
var layerClient: LYRClient!
override func viewDidLoad() {
let appDelegate = UIApplication.sharedApplication().delegate as!AppDelegate
self.layerClient = appDelegate.layerClient
self.conversationListViewController = ConversationListViewController(layerClient: appDelegate.layerClient)
self.conversationListViewController.displaysAvatarItem = true
self.navigationController!.pushViewController(self.conversationListViewController, animated: true)
}
}
But this is not working. And giving this kind of effect: the ConversationViewController is not loaded in UINavigationController. I am not sure if I am doing it the correct way. I'm searching for the correct way, but unable to find.
I Solved it. I dragged new NavigationViewController and added ConversationListViewController to rootviewController.I think i should try this first. Anyways thanks guys for your help.
Because you want to do this programatically:
You need to manually initialize the controller before stacking it up on the Navigation Controller. Try this:
navigationController?.pushViewController(self.conversationListViewController.init(), animated: true)