Segue from UITableView to UITabBarController - ios

I am creating an app that allows the user to see a random quote everyday. In this app, the user is asked 3 questions before being able to actually use the app. The last question is a simple "What is your favorite category/topic". With this prompt, the user will tap a cell and be brought to a Tab Bar Controller with the first "Child" view controller being the quote itself.
Problem:
I want the user to be able to tap a UITableViewCell and the one they tap effects which TabBarController they are brought to.
That is the photo with the errors I am running into so far. Here is the code.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "bookSegue")
{
let bookQuoteTabBar = segue.destinationViewController as! UITabBarController
let bookQuoteScreen = bookQuoteTabBar.viewControllers?[0] as? bookQuoteScreen
}
else if(segue.identifier == "businessSegue") {
let businessQuoteTabBar: UITabBarController = segue.destinationViewController as! UITabBarController
let businessQuoteScreen = businessQuoteTabBar.viewControllers?[0] as? businessQuoteScreen
}
}
Eventually, there will be more topics, meaning more segues. But for now, I'm starting with two
The segues for each TabBarController are:
"bookSegue"
"businessSegue"
The Tab Bars are:
"bookQuoteTabBar" and "businessQuoteTabBar"
The First "Child" View controllers are:
"bookQuoteScreen"
"businessQuoteScreen"
Should I have written something else? Did I correctly name the Segues, identities, and classes of each object? If you need more information or references, comment what I should add and I will add it within minutes. Thank you in advance!
---------Recent edits---------
BooksQuoteScreen:
import Foundation
import UIKit
class BooksQuoteScreen: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
BusinessQuoteScreen:
import Foundation
import UIKit
import Social
class BusinessQuoteScreen: UIViewController {
//============================//
//********** Outlets *********//
//============================//
let utility = Utility()
#IBOutlet weak var quoteDisplay: UILabel!
#IBOutlet weak var authorDisplay: UILabel!
#IBOutlet weak var quoteBackground: UIImageView!
...
}

The errors in your screenshot ("Use of undeclared type ....") indicate that Xcode does not recognise bookQuoteScreen and businessQuoteScreen as valid types. In the highlighted lines, eg.
let bookQuoteScreen = bookQuoteTabBar.viewControllers?[0] as? bookQuoteScreen
the type (specified after "as? ") must match up with the class name defined in your .swift files. Check very carefully that the names used match the class names (presumably) defined in "BusinessQuoteScreen.swift" and "BooksQuoteScreen.swift". Without seeing the contents of those files, I can't be certain, but I suspect the leading character needs to be upper case (it should be for class names), and you might need an "s" in
"BooksQuoteScreen":
let bookQuoteScreen = bookQuoteTabBar.viewControllers?[0] as? BooksQuoteScreen
and
let businessQuoteScreen = businessQuoteTabBar.viewControllers?[0] as? BusinessQuoteScreen

Related

When trying to segue to a view controller from a table view i get this error: Unexpectedly found nil while unwrapping

I have a segue named "hydrogenSegue" from a "hydrogenBoxButton" to a "Hydrogen" view controller. However, I also wanted to implement a table view so I could search for an element. I tried to make the code so when the cell is clicked it will segue over to the element's view. I used hydrogen as an example here.
In my main ViewController.swift file, I have this to transfer the data:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//If identifier equals the hydrogen element go to the elements Swift file
if segue.identifier == "hydrogenSegue" {
let hydrogenAtomicNumberPassing = segue.destination as! hydrogenViewController
hydrogenAtomicNumberPassing.hydrogenAtomicNumberPassed = hydrogenAtomicNumber
let hydrogenAtomicMassPassing = segue.destination as! hydrogenViewController
hydrogenAtomicMassPassing.hydrogenAtomicMassPassed = hydrogenAtomicMass
}
}
In the hydrogenViewController.swift file I have this:
import UIKit
class hydrogenViewController: UIViewController {
var hydrogenAtomicNumberPassed: Int!
var hydrogenAtomicMassPassed: Float!
#IBOutlet weak var hydrogenInformationLabel: UILabel!
#IBOutlet weak var hydrogenAtomicNumberLabel: UILabel!
#IBOutlet weak var hydrogenAtomicMassLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//Setting the background color
self.view.backgroundColor = UIColor.gray
//Converting hydrogen's atomic number from an Int to a String
let hydrogenAtomicNumberString = String("\(hydrogenAtomicNumberPassed!)")
hydrogenAtomicNumberLabel.text = "Atomic Number: \(hydrogenAtomicNumberString)"
//Converting hydrogen's atomic mass from a Float to a String
let hydrogenAtomicMassString = String("\(hydrogenAtomicMassPassed!)")
hydrogenAtomicMassLabel.text = "Atomic Mass: \(hydrogenAtomicMassString)"
}
}
I am getting the error at:
let hydrogenAtomicNumberString = String("\(hydrogenAtomicNumberPassed!)")
I'm assuming it would happen to this line also if I fix only that line:
let hydrogenAtomicMassString = String("\(hydrogenAtomicMassPassed!)")
I have this code in my "searchViewController" (the .swift file used for the table view):
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("row selected : \(indexPath.row)")
if indexPath.row == 0 {
let hydrogenSearchSegue = UIStoryboard(name:"Main",
bundle:nil).instantiateViewController(withIdentifier: "hydrogenView") as!
hydrogenViewController
self.navigationController?.pushViewController(hydrogenSearchSegue,
animated:true)
}
}
When I click on the "Hydrogen" cell in the table view it crashes to this error:
Hydrogen cell
The crash
When I click on the "H" button in this image it will take me to the hydrogen view controller:
Image of the Hydrogen Button in the simulator (Top Left)
Image of the Hydrogen View Controller
I want the hydrogen cell to segue over to the hydrogen view controller just like the button can.
When this same issue came up earlier I just had an issue with the name of the segue in the storyboard. However, because there is no visible segue from the table view, I don't know how to fix the issue.
I've tried this:
performSegue(withIdentifier: "hydrogenSegue", sender: nil)
I was thinking that I could just reuse the "hydrogenSegue" from the button to the view controller but I get a SIGABRT error. It just says that there is no segue with the name "hydrogenSegue." It would be best if I could just reuse that segue in a way because everything is already connected but I now found out that the "searchViewController" can't recognize the segue. Any help is appreciated and my main goal is to just get the cell that is clicked on to move over to the element's designated view. I tried to provide as much information as possible without making it to long and if there is any more information needed, I should be able to provide it.
well. first answer
in your hydrogenViewController try with this lines.
var hydrogenAtomicNumberPassed: Int?
var hydrogenAtomicMassPassed: Float?
override func viewDidLoad(){
super.viewDidLoad()
self.viewBackgroundColor = .gray
}
override func viewWillAppear(){
super.viewWillAppear()
if let number = hydrogenAtomicNumberPassed
{
hydrogenAtomicNumberLabel.text = "Atomic Number: \(number)"
}
if let mass = hydrogenAtomicMassPassed
{
hydrogenAtomicMassLabel.text = "Atomic Mass: \(mass)"
}
}
Now, the segues only "lives" between a couple viewControllers, if you have a third view controller, the last will not recognize him.
other thing, you are using segues and navigation controller, from my point of view, it's a bad idea mixed both, I mean, there are specific apps that can use both ways to present views, only is a advice.
if you want to pass data with pushviewcontroller only use this line
if indexPath.row == 0 {
let hydrogenSearchSegue = UIStoryboard(name:"Main",bundle:nil).instantiateViewController(withIdentifier: "hydrogenView") as! hydrogenViewController
hydrogenSearchSegue.VAR_hydrogenViewController = YOURVAR_INYOURFILE
self.navigationController?.pushViewController(hydrogenSearchSegue, animated:true)
}
tell me if you have doubts, and I will try to help you.

Many buttons to a single view controller

im developing an app that utilises many buttons( possibly 20 buttons) on one primary view controller that can are all able to activate a singular picker view within a pop up on a seperate view controller. i don’t think the answer is lots and lots segues. Is there a better approach I should be considering?
I’m thinking - some kind of multiuse segue that can be activated by any of the buttons, but nonidea how this is done.
Appreciate any advice
Mike
Set up all buttons to same action such as:
#IBAction func keyPressed(_ sender:UIButton){
// use button title string
self.keyString = sender.titleLabel?.text as! String
// or tag
self.keyTag= sender.tag?
self.performSegue(withIdentifier: "TheSegue", sender: self)
}
Then you would want to set up the View Controller that you are going to navigate to based on the state of the sender. So you would override the prepare:forSegue method as below.
override func prepare(for segue:UIStoryboardSegue, sender:Any?) {
let destController = segue.destination as! Dest_Controller_Class
// use tag or keyTitle to set controller attributes
// before view is shown
destController.keyTag = self.keyTag
destController.keyString = self.keyString
}
Now once you've navigated to the Dest_Controller_Class, you will have the properties of the button pressed locally in the view controller and could update the view as you see fit:
class Dest_Controller_Class: UIViewController {
var keyString: String?
var keyTag: Int?
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
if (keyString != nil) {
label.text = keyString;
// or likewise use tag
} else {
label.text = "keyString not set"
}
}
}

how to DownCasting UIViewController Swift

base ViewController
import UIKit
class SubViewPost: UIViewController {
#IBOutlet weak var content: UILabel!
#IBOutlet weak var recommendCount: UILabel!
#IBOutlet weak var recommendButton: UIButton!
var postInfo:PostInfo!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
child ViewController
import UIKit
class SubViewOne: SubViewPost {
#IBAction func likeWorry(_ sender: Any) {
Option.recommend(postInfo: postInfo, mRecommendCount: recommendCount, mRecommendButton: recommendButton)
}
}
and another child viewController
import UIKit
class SubViewTwo: SubViewPost {
override func viewDidLoad() {
recommendCount.alpha=0
recommendButton.alpha=0
}
}
i want add subviewOne or SubViewTwo
My ParentView
var subViewPost:SubViewPost
if postType == 1{
subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewOne
}else{
subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewTwo
}
containerView.addSubview(subViewPost.view)
raise error
Could not cast value of type
'MyApp.SubViewPost' (0x101151728) to 'MyApp.SubViewOne' (0x10114d9d0).
2018-07-10 14:40:56.007436+0900 MyApp[7207:209932]
Could not cast value of type 'MyApp.SubViewPost' (0x101151728) to 'MyApp.SubViewOne' (0x10114d9d0).
how to chagne view controller by According to postType
SubView One have Recommned
but SubView Two haven't Recommend
SubView 1,2 have same UI
The UViewController class for your scene "SubViewPost" in your storyboard is set to SubViewPost and that is what instantiateViewController will be returning. You cannot downcast an instance of SubViewPost to SubViewOne or SubViewTwo.
You could define two identical scenes in your storyboard, each with the appropriate view controller class, but that would require a lot of duplication.
Since the only difference is the visibility of the recommendButton and recommendCount elements, why not just handle that via a property:
var subViewPost = storyboard?.instantiateViewController(withIdentifier: "SubViewPost") as! SubViewPost
subViewPost.recommendVisible = (postType == 1)
SubViewPost.swift
var recommendVisible = true
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
recommendCount.isHidden = !recommendVisible
recommendButton.isHidden = !recommendVisible
}
The error message is clear. When you say
storyboard?.instantiateViewController(withIdentifier: "SubViewPost")
what you get from the storyboard is a view controller whose class is SubViewPost. You cannot wave a magic casting wand and claim that it is a SubViewOne instead; it isn't.
If you wanted this view controller to be a SubViewOne, then you should have declared it as a SubViewOne in the storyboard in the Identity inspector.
I think I see what you are trying to do, and why you are confused about why you can't do it this way.
What's in the storyboard is an instance, not a class. Yes, it is an instance of some class, but it is an instance of that class. So when you design the interface in the storyboard, you are designing the interface associated with that one instance of that one class.
If your goal is to have a single interface associated with multiple classes, the interface must be generated in code or loaded from a View .xib file — not designed in a storyboard.
However, you would be better off not trying to use subclassing in this situation in the first place. What I do in a similar situation is give my view controller an enum property that says which "kind" of view controller it is, and obey accordingly in code. That a way, a single class serves multiple purposes.

Text Field and Label Editing Errors

Before I get started, I have to say that this project marks the first in-depth use of Swift and XCode in my life. I started it about a week ago (and am honestly impressed with how far I've gotten). I do not know too much about what I'm doing but I'm willing to learn.
Now, onto my question.
I'm trying to get a text field from one view controller to change a label from another. I thought I did it right, but it kept throwing syntax errors and such at me. After fixing that, I would run the code and get a SIGABRT error. Here's my code.
Here's the label, under BasicViewController (this isn't all that's in BasicViewController, I just cut out what I thought was pertinent)
class BasicViewController: UIViewController {
#IBOutlet weak var NameField: UILabel!
var NameText = String()
override func viewDidLoad() {
super.viewDidLoad()
NameField.text = NameText
}
And here's the text field, under EditCharController. This is where the SIGABRT error happens. (also, same thing with the lack of code.)
class EditCharController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
#IBOutlet weak var NameTextField: UITextField!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var NameTextDest : BasicViewController = segue.destinationViewController as! BasicViewController //Specifically, this is the line that it happens at.
NameTextDest.NameText = String(NameTextField.text)
}
}
Currently, XCode is telling me to change the var label to the let label, but even if I do that, it spits out this error.
Could not cast value of type 'UITabBarController'(?!?) (0x10310c8b0) to 'Project.BasicViewController' (0x1019a0060).
Last I checked, I didn't reference the UITabBarController anywhere in the code. Why am I getting this message?
Also any suggestions as to good in-depth tutorials will be very much appreciated.
Try
let index = 0 // change this to the tab index of the BasicViewController.
let NameTextDest : BasicViewController = (segue.destinationViewController as! UITabBarController).viewControllers[index] as! BasicViewController
NameTextDest.NameText = NameTextField.text!

Crash while adding UIImage from code

I'm a Swift beginner and I'm writing an app that will show a list of things. When you click on one of them, you'll get detailed information about it and a photo. However, I've got one problem - I've got code to show the image, but when I click in simulator it crashes.
Text that is on the bottom of xcode when the app crashes:
fatal error: unexpectedly found nil while unwrapping an Optional value
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var detail = segue.destinationViewController as! detailSkinsViewController
detail.skinNameLabel = self.skin
if (self.skin == "AK47 | Wasteland Rebel") {
detail.skinImg.image = UIImage(named: "s495fn")
}
}
Thanks!
You should look at the crash stack to see the actual line. There are a couple of places you could have trouble:
var detail = segue.destinationViewController as! detailSkinsViewController
This requests a crash if destinationViewController is not of class detailSkinsViewController. (Swift classes should always begin with a capital letter. This should also be a let, not var. You never modify it.) Using if-let here would be much safer.
detail.skinImg.image = UIImage(named: "s495fn")
It's very unclear what these are, but if skinImg is a UIImageView!, then you would expect this to crash if it the destination NIB has not been loaded yet (which is likely). You generally should never reach into other objects IBOutlets for exactly this reason. (This is also a good reason to use ? rather than ! for IBOutlets. That would lead to just "nothing happens" rather than a crash.)
Rather than messing with another view controllers outlets, you should create a UIImage? property on the view controller itself. In its didSet, update the UIImageView if the view is loaded (isViewLoaded). During viewDidLoad, initialize the UIImageView using the property. This way, you have a clear API for others to set the image that doesn't expose your internal subviews (which are implementation details).
As an example:
class ViewController: UIViewController {
#IBOutlet private var imageView: UIImageView?
var image: UIImage? {
didSet(newImage) {
self.imageView?.image = newImage
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.imageView?.image = self.image
}
}
You should check your optional values,
i did not see your skin variable definition but i think that code below will solve your problem
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let detail = segue.destinationViewController as! detailSkinsViewController {
detail.skinNameLabel = self.skin
if (self.skin == "AK47 | Wasteland Rebel") {
detail.skinImg.image = UIImage(named: "s495fn")
}
}
}
This Error comes normally when trying to unwrap a value of an optional variable which has no value at all.
First check your optional variables and debug line by line to see if any of the Optional variables has no value at all which you had been trying to unwrap

Resources