I have a view controller OtherUserAccountViewController containing a button with a "profile picture" as its background. When this button is tapped, I would like to push a new view controller ImageTappedViewController onto the stack to simply present a bigger ImageView of this said profile picture. Please note the Storyboard identifier for the screenshot seen below is in fact "imageTapped" and the class is ImageTappedViewController
Below is my function for instantiating and pushing the new view controller:
In OtherUserAccountViewController.swift:
#IBAction func profilePicButtonTapped() {
let sb = UIStoryboard(name: "SuccessfulLogin", bundle: nil) //SB name: SuccessfulLogin
let cc = (sb.instantiateViewController(withIdentifier: "imageTapped")) as! ImageTappedViewController
if cc.imageView == nil || cc.imageView == UIImage() {
print("Nil") //<- Nil is printed upon firing this function
} else {
print("not nil")
}
//cc.imageView.image = self.profilePicButton.currentBackgroundImage <- breaks because the imageView is nil
self.navigationController?.pushViewController(cc, animated: true)
}
ImageTappedViewController.swift:
import UIKit
class ImageTappedViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
I am totally lost as to why this imageView is returning nil.
I set the imageView to display the lovely Taylor Swift on default as seen below; but regardless, nil is being returned.
Any help is much appreciated!
When view controllers are first initialized their IBOutlets will not be initialized. Only after viewDidLoad will all of their outlets be non-nil. Pass the image as a UIImage then in viewDidLoad set image view's image property.
Related
So all my controller's are done programmatically to avoid segues and that sort of complicated stuff.
I have a viewcontroller (Call it ProfileViewController) that downloads data from the network.
So I have a method in ProfileViewController that instantiates a single storyboard file with a static tableview with cells that have textfields in them. Here is the method:
ProfileViewController:
func userSelectedUpdateProfile() {
// Obtain reference to the only storyboard file named EditProfileSB
let storyboard = UIStoryboard(name: "EditProfileSB", bundle: nil)
// Since the Tableview is embedded in a navigation controller (with ID set to "navigationID")
if let parentNavigationController = storyboard.instantiateViewController(withIdentifier: "navigationID") as? UINavigationController {
// Now find the embedded TableViewController and access it's properties to pass to.
if let childEditController = parentNavigationController.topViewController as? EditProfileTableViewController {
// ! Error here ! Found nil when doing this.
childEditController.nameTextfield.text = "Passed this to static cell"
}
present(parentNavigationController, animated: true, completion: nil)
}
}
So the code itself is self-explanatory to what I am trying to achieve here. The TableView is embedded in a Navigation (done on storyboard with "Editor > Embed In") so on the 2nd nested if let statement I am now checking to find that Edit controller and access its properties (nameTextfield).
I get a crash when I attempt to access the nameTextField.text property. This textfield is set using storyboard. Here is that EditProfileTableViewController:
class EditProfileTableViewController: UITableViewController {
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
// Other methods ...
}
Here is the storyboard flow layout.
Am I missing something here? I keep getting a crash on childEditController.nameTextfield.text = "Passed this to static cell" on the method userSelectedUpdateProfile().
If your View Controller still not call viewDidLoad().
your textfield is not create.
#IBOutlet weak var nameTextfield: UITextField!
you can see it's attribute is weak here.
Try create a value and pass text to the value. Then in viewDidLoad(), you can set the value to your textField
i’m working in swift and i’m trying to use the .frames to check if 2 objects of type CGRect intersect.
i have my View Controller Class and a CircleClass, the CircleClass creates a circle that has gesture recognition so i can drag the circles that i create where i want to, now i want to add the option that if at the end of the drag the subview intersects my trashimageView (image view that will always be in the low-right corner of the view for all devices it's like a trashcan) it can delete the circle or subView.
the problem is that when i try to call trashImageView.frame in a function “deleteSubView” that i’ve created in the View Controller i get nil and my app crashes.
But if the IBOutlet is in the VC and my function is defined in my VC, also i can call the trashImageView.frame (CGRect Value) in the viewDidLoad and there is fine, but not in my function, why do i get nil for this value??
class ViewController: UIViewController {
#IBOutlet var trashImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
//here i can print the CGRect value just fine
print("my imageView init: \(trashImageView.frame)")
}
func deleteSubView(subView: UIView){
// Here i get nil from the trashImageView.frame
if (subView.frame.intersects(trashImageView.frame)) {
print("intersection")
subView.removeFromSuperview()
}
}
}
i've checked that the Nil value is from the 'trashImageView.frame' and that the connection with the storyboard is good.
i call the function ‘delete subView’ from another class but should that matter? i don’t understand what is the error here, why do i get nil? help please.
Since your UIViewController is declared and instantiated using storyboard my guess is that you are creating the view controller using it's no arg initializer, i.e.: let controller = MyController() if you must create an instance of the controller programmatically do so by obtaining a reference to the Storyboard that contains the controller, i.e like this:
NOTE: Here I'm using "MyController" as the name of the class and the identifier that has been set in the storyboard.
func createMyController() -> MyController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "MyController")
return controller as! MyController
}
I'd also add a guard for view load state in your deleteSubview(:subView) method, so something like this:
func deleteSubView(subView: UIView) {
guard isViewLoaded else { return }
// Here i get nil from the trashImageView.frame
if (subView.frame.intersects(trashImageView.frame)) {
print("intersection")
subView.removeFromSuperview()
}
}
I am trying to pass a data model from my initial view controller to the view controller that is now on screen. I have a container view that shows a pdf. When I run the code the document that is passed into the container is nil for some reason.
I have used the debugger and watch it get set in the initial view controller, but when the next storyboard is loaded that var is now nil for some reason. I have tried it in viewDidAppear but I get the same issue.
My initial view controller (the homepage)
let documentGet = Data.documentModel[selectedRow - 1]
let storyboard = UIStoryboard(name: String(describing: NoteTakingViewController.self), bundle: nil)
let vc = storyboard.instantiateInitialViewController() as! NoteTakingViewController
vc.documentSet = documentGet
//self.navigationController?.pushViewController(vc, animated: true)
//self.present(vc, animated: true, completion: nil)
self.show(vc, sender: selectedRow)
The next view controller for the storyboard where I pass the previous vc.documentSet = documentGet
let pdfViewer = PDFView()
#IBOutlet weak var PDFClass: PDFViewClass!
var documentSet:DocumentModel!
override func viewDidLoad() {
super.viewDidLoad()
PDFClass.setDocument(document: self.documentSet) <----(this is where the error occurs)
self.title = documentSet.title
navigationController?.navigationBar.prefersLargeTitles = false
}
This is the view controller for the container view. It is a custom class since I have tried to get it to work on the previous controller but it will block the tool bar that I am trying to figure out underneath the navigation bar (yes directly under the navigation bar)
private func configurePDF() {
pdfViewer.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(pdfViewer)
}
func setDocument(document: DocumentModel!) {
configurePDF()
let doc = PDFDocument(url: document.url)
pdfViewer.document = doc
}
I expect the pdf data to go from home page -> view controller -> container view controller. But I get nil on the view controller. I guess I just do not understand how the UIView are loaded. My thought process is the that the var in the view controller is set with vc.documentSet = documentGet then when that view is up it will pass the var to the container view.
I hope this is not something super simple but the way swift works is different from my experience with java.
You can solve this with some defensive programming and a property observer:
let pdfViewer = PDFView()
#IBOutlet weak var PDFClass: PDFViewClass!
var documentSet: DocumentModel? {
didSet {
self.title = documentSet?.title
if documentSet != oldValue {
setDocument(to: documentSet)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
setDocument(to: documentSet)
navigationController?.navigationBar.prefersLargeTitles = false
}
private func setDocument(to document: DocumentModel?) {
guard let validDocument = document else { return }
PDFClass?.setDocument(document: validDocument)
}
I'm getting nil when unwrapping an optional value with GADBannerView..
I setup my ad banner like this, in FlashViewController.swift..
class FlashViewController: UIViewController {
#IBOutlet weak var bannerView: GADBannerView!
and then in ViewDidLoad:
func initAdMobBanner() {
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716"
bannerView.rootViewController = self
bannerView.load(GADRequest())
}
bannerView has an outlet in storyboard to Root View Controller, which is class FlashViewController.
Then in TableViewController.swift I have my purchase button. Purchase button runs:
FlashViewController().HideMyBanner();
The function HideMyBanner is in FlashViewController and will run this code:
if bannerView != nil {
print("bannerview Contains a value!")
bannerView.isHidden = true
} else {
print("bannerview Doesn’t contain a value.")
}
The issue is, if I create a button directly in FlashViewContorller.swift and run the same function, bannerView contains a value and can be hidden.. If I call the function from TableViewController.swift, it returns nil, (or crashes if I try to hide bannerView... I feel like I missing something easy here, but already spent a long time trying to figure it out...
By using this line FlashViewController().HideMyBanner(); you are creating new object of FlashViewController. so it will crash.you need to use the object of FlashViewController which is already created and loaded in memory.
I think you need to pass the reference of FlashViewContorller to TableViewController
If your TableViewController is load from FlashViewContorller than you need to create reference FlashViewContorller in TableViewController like this way.
class TableViewController: UIViewController {
var objFlashViewContorller : FlashViewContorller?
override func viewDidLoad() {
super.viewDidLoad()
//This is the UILabel
}
func purchasebuttonClick() {
objFlashViewContorller?.HideMyBanner();
}
}
While setup Navigation FlashViewContorller to TableViewController you need to pass reference.
let tableViewController = self.storyboard!.instantiateViewController(withIdentifier: "TableViewController") as! TableViewController
tableViewController.objFlashViewContorller = self
self.navigationController?.pushViewController(tableViewController, animated: true)
OR
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "TableViewController" {
let vc = segue.destinationViewController as! TableViewController
vc.objFlashViewContorller = self
}
Don't use FlashViewController().HideMyBanner();
I think you need to use this in in TableViewController.swift -> my purchase button method.
self.revealViewController.frontViewController.HideMyBanner()
I used Notifications to finally work this out... Controller 2 sends a notification that a purchase has been made, and Controller 1 observes and waits for this notification, then takes care of hiding the banner in Controller 1.
https://blog.bobthedeveloper.io/pass-data-with-nsnotification-in-swift-3-73743723c84b
I am re-writing a tutorial converting the code from Objective-C to swift. The app moves from VC one where there is 3 sliders (Red, Green and Blue) that set the background colour, a label of the colour name and a button that links to the second VC. In the second VC the colour from the first VC is used as the background and the user has a chance to name the colour.
When the user enters the colour name it should return the new colour name to the orginal VC and the label that shows the colour name should show the text entered.
The following is the code that is causing issue:
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
ViewController().colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
The error "fatal error: unexpectedly found nil while unwrapping an Optional value" is generated. However debugging nameEntry.text has a string in it.
I'm a little stumped. I could try and do a prepare for unwind segue but it is meant to be a tutorial app.
Cheers
ViewController() actually creates a new instance of your ViewController. This is not a reference to the already existing ViewController. What you can do is create a weak variable pointing to first ViewController inside the second ViewController and set it at prepareForSegue or when the second View controller is shown.
class SecondViewController : UIViewController {
weak var firstViewController : ViewController?
// Other code
func textFieldShouldReturn(nameEntry: UITextField) -> Bool
{
firstViewController?.colourLabel.text = nameEntry.text
nameEntry.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
return true
}
}
Inside First View Controller prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SecondViewController" {
let secondViewController = segue.destinationViewController as SecondViewController
secondViewController.firstViewController = self
}
}
It's possible that the view controller returned by ViewController() has not yet loaded its views. You could try checking this in a setter function and storing it for later use once the views have been loaded.
class VC : UIViewController {
#IBOutlet weak var colourLabel: UILabel!
var savedLabelText: String?
override func viewDidLoad() {
super.viewDidLoad()
self.colourLabel.text = self.savedLabelText
}
func setColorLabelText(label: String) {
if self.isViewLoaded() {
self.colourLabel.text = label
}
else {
self.savedLabelText = label
}
}
}