Changing visibility if UIView in ViewControllerTwo from ViewControllerOne - swift

I am using XLPagerStrip to show two tabs. I have a search bar to search on each ViewController. I have I filter button that should be clicked from ViewcontrollerOne to change the visibility of a UIView in ViewControllerTwo. Here is what I have implemented but I'm getting
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional on the view
ViewcontrollerOne
viewControllerTwo.getFilterStatus(status: isFilter)
ViewControllerTwo
func getFilterStatus(status: Bool){
if (status) {
viewFilter.isHidden = false
} else {
viewFilter.isHidden = true
}
}

Related

'Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional' value when I try to close all keyboards in a view [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 4 years ago.
I have put a full-screen size invisible button behind of all objects(like textfield, picker.. ) to close opened keyboard. I call below function when the button is clicked:
func hideKeyboard() {
for view in self.contentViewOutlet.subviews {
if let tField = view as? UITextField {
tField.resignFirstResponder()
}
}
}
but I get this error after I click the button:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I roughly know what does it mean but I couldn't write a solution. (Actually, this hideKeyboard() function was working fine. It starts to give an error after I add UIPickerView)
Your contentViewOutlet is an Outlet so it might be nil, but it's implicitly unwrapped. And you get this error because when you tap a button, this object is nil. To avoid the crash, change your code to
func hideKeyboard() {
guard let contentView = self.contentViewOutlet else { return }
for view in contentView.subviews {
if let tField = view as? UITextField {
tField.resignFirstResponder()
}
}
}
After that, your method won't do anything if contentViewOutlet is nil.

How to detect if a pushed viewcontroller appears again?

assuming I have a viewcontroller (vcA) that pushes QRCodeScannerViewcontroller (vcB). When (vcB) scanned something, It will push ResultviewController (vcC).
-Those 3 views is connected to a UInavigation controller
-the user clicks on the back button on (vcC)
my question is:
1)how can I know if (vcB) is visible without changing code on (vcB)? (vcB) is a pod
2)where will I put this code? I can only access (vcA)
i tried adding this code on (vcA) but nothing happened
override func viewDidDisappear(_ animated: Bool) {
if (vcB.isViewLoaded && (vcB.view.window != nil)){
print("vcb did appear!")
}
}
To know if an instance of cvB's class exists in the navigation stack, you could use this piece of code:
let result = self.navigationController?.viewControllers.filter({
if let vcB = $0 as? UIViewController { // Replace UIViewController with your class, for example ViewControllerB
return true
}
return false
})
if result.isEmpty {
print("An instance of vcB's class hasn't been pused before")
} else {
print("An instance of vcB's class has been pused before")
}

How to check for self with contains() in Swift?

I'm trying to check if the back button on my view controller was pressed but I'm having a hard time detecting this in Swift.
With this code:
if (contains(self.navigationController?.viewControllers, self)) {
println("Back button not pressed")
} else {
self.updateSearchQueryModel()
}
I am getting the error:
Could not find an overload for contains that accepts the supplied arguments.
I did get the result that I wanted in another fashion but I am still confused as to why this error is happening.
Why is this happening? Can I not check if self exists in an array?
Source of original code in Objective C that I couldn't translate to Swift:
Setting action for back button in navigation controller
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
// back button was pressed. We know this is true because self is no longer
// in the navigation stack.
}
[super viewWillDisappear:animated];
}
Please do not tell me how to detect that the back button was pressed. I already figured that out here.
Source of solution to objective: Detecting when the 'back' button is pressed on a navbar
If you look at the declaration of the viewControllers property, you notice that it's [AnyObject]! and not [UIViewController]!.
The contains function requires that the sequence element implements the Equatable protocol, which AnyObject doesn't.
The solution is to make an explicit downcast of that array, using optional binding:
if let viewControllers = self.navigationController?.viewControllers as? [UIViewController] {
if (contains(viewControllers, self)) {
println("Back button not pressed")
} else {
self.updateSearchQueryModel()
}
}
I'm new in Swift but try this:
if let controllers = self.navigationController?.viewControllers as? [UIViewController] {
if contains(controllers, self) {
DLog("!")
}
}
You're getting error because you're passing optional as contains()'s first argument

Detail View Controller transition from Master View Contoller

I am getting "unexpectedly found nil while unwrapping an Optional value" because in my code below I am trying to assign value to webview before its initialize. I am trying to transition from Master to Detail view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let controller = (segue.destinationViewController as UINavigationController).topViewController as DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Detail View Code:
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
It is failing because my Webview in Nil. How do I come around this situation where my outlets are not initialized while setting them.
Please help.
Thanks.
Stop and think about the order in which things happen:
prepareForSegue - The destination view controller exists, but that's all. It has no view and its outlets have not been set. You can set its non-outlet properties but that's all you can do.
The segue starts to happen.
The destination view controller gets viewDidLoad. Now it has a view and its outlets are set.
The segue completes and the destination view controller gets viewWillAppear: and later, viewDidAppear:. Now its view is actually in the interface.
So clearly you cannot permit configureView to assume that the web view exists, because the first time it is called, namely in prepareForSegue, it doesn't exist. configureView needs to test explicitly whether self.webView is nil, and if it is, it should do nothing:
func configureView() {
// Update the user interface for the detail item.
if self.webView == nil { return } // no web view, bail out
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
After that, everything will be fine. viewDidLoad will subsequently be called, and configureView will be called again - and this time, both detailItem and the web view exist, so all will be well.

Using a UIViewController as the default value for an optional parameter, but I get the "X does not have a member named Y" error

So I have been having fun with default parameter values.
class containerViewController: UIViewController {
var detailView:UIViewController?
override func viewDidLoad(){
super.viewDidLoad()
detailView = anotherViewController()
}
func hideDetailView(vc:UIViewController? = detailView){ // <- THIS LINE
// code
}
}
The line Ive marked produces an error:
'containerViewController.Type' does not have a member named 'detailView'
Ive been reading online, including this question, but I cant seem to figure out how to fix this.
What I want is to be able to use hideDetailView() and if I send in a specific view controller as a parameter to that function, it hides that specific view controller. If I dont send any parameter, it just hides the current view controller that is held in the detailView parameter.
How can I achieve this?
You can use nil for the default value, and check if nil in the body.
func hideDetailView(vc:UIViewController? = nil){ // <- THIS LINE
let vc_ = vc ?? detailView
// code
}
But In this case, you can't distinguish following calls:
// passing `nil` as Optional<UIViewController>
let vc:UIViewController? = nil
container.hideDetailView(vc: vc)
// use default value
container.hideDetailView()
If you don't like that, you can use UIViewController??:
func hideDetailView(vc:UIViewController?? = nil){
let vc_ /*: UIViewController? */ = vc ?? detailView
// code
}