The text in the Modal View Controller does not change in the view controller - ios

I am a beginner in iOS.
I have a problem that I made a class named ModalViewController. To make this as a common class, I didn't give any text to the textView in this class. So I tried to change the text when I present this viewController.
But I am getting an error. How can I fix it?
In addition, how can I change the margin value in text view?
Main.storyboard
MainViewController.swift
import UIKit
var modalview : ModalViewController!
class ViewController: UIViewController {
...
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController")
myAlert.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext
myAlert.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
modalview.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
self.present(myAlert, animated: true, completion: nil)
}
ModalViewController.swift
import Foundation
import UIKit
class ModalViewController : UIViewController {
#IBOutlet weak var modalCustomAlert: UITextView!
#IBOutlet weak var cancelButton: UIButton!
#IBOutlet weak var okButton: UIButton!
#IBAction func cancelPress(_ sender: Any) {
}
#IBAction func okPress(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
modalCustomAlert.layer.cornerRadius = 8
}
#IBAction func modalbutton(_ sender: Any) {
}
}
Edited start
I tried to assign a value but there was no change in the error.
var modalview = ModalViewController()
...
#IBAction func Onclick(_ sender: Any) {
...
modalview.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
}
Edited TWO start
I've tried this already. But when I saw the answer from #PGDev, I tried it again, but it showed the same error.
#IBAction func Onclick(_ sender: Any) {
...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.modalCustomAlert.text = "test test test text" // Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
self.present(myAlert, animated: true, completion: nil)
}

Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
modalview.modalCustomAlert.text = "test test test text"
The exception is because either modalview is nil or modalCustomAlert is nil.
1. You've created modalview as implicitly unwrapped optional like,
var modalview : ModalViewController!
Check if you've assigned any value to modalview before accessing it. If you haven't this will result in crash.
2. Next, you have created an outlet of modalCustomAlert,
#IBOutlet weak var modalCustomAlert: UITextView!
This is also an implicitly unwrapped optional. Check if the outlet is connected properly. If it isn't it will raise the runtime exception.
If an implicitly unwrapped optional is nil and you try to access its
wrapped value, you’ll trigger a runtime error. The result is exactly
the same as if you place an exclamation mark after a normal optional
that doesn’t contain a value.
Edit-1:
var modalview = ModalViewController()
The above code won't connect the outlets in ModalViewController. That's the reason modalCustomAlert is nil and result in exception.
Create modalview using storyboard like,
let modalview = storyboard.instantiateViewController(withIdentifier: "ModalViewController")
One question though. What's the reason to create modalview and myAlert separately if they both are of type ModalViewController?
Edit-2:
I don't think there is even a need to create modalview. Simply use myAlert view at the line where you're getting the exception. So the code for ViewController goes like,
class ViewController: UIViewController {
//....
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.modalCustomAlert.text = "test test test text"
self.present(myAlert, animated: true, completion: nil)
}
}
Edit-3:
Got that. Just didn't realise that you're accessing modalCustomAlert before the outlets are created.
First of all, in ModalViewController create a property text. And in viewDidLoad() add this text as modalCustomAlert.text, i.e.
class ModalViewController : UIViewController {
//rest of the code....
var text: String? //here....
override func viewDidLoad() {
super.viewDidLoad()
modalCustomAlert.layer.cornerRadius = 8
self.modalCustomAlert.text = text //here....
}
}
Now, when creating alertView, replace
myAlert.modalCustomAlert.text = "test test test text"
with
myAlert.text = "test test test text"

Please add one new variable String in Your ModalViewController
class ModalViewController : UIViewController {
var objTextViewString:String = ""
override func viewDidLoad() {
super.viewDidLoad()
self.modalCustomAlert.text = self.objTextViewString
}
}
On ModelViewController Present Please pass String On added String Variable..That's It..!!
class ViewController: UIViewController {
//....
#IBAction func Onclick(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let myAlert = storyboard.instantiateViewController(withIdentifier: "ModalViewController") as! ModalViewController
myAlert.modalPresentationStyle = .overCurrentContext
myAlert.modalTransitionStyle = .crossDissolve
myAlert.objTextViewString = "test test test test"
self.present(myAlert, animated: true, completion: nil)
}
}

Related

Why I get a uncaught expetion when I want save a value

I'm just learning XCode and now I have the following problem, I've tried a few things but I can't get any further at this point, does anyone have an idea of what this could be.
I have two ViewControllers. With the first ViewController you have the possibility to open a popup for user input via a button (second ViewControler). After entering the user, you can save the user with a save button and you should get back to the main page. Then I always get the error
"Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PwGen.ViewControllerUser tfUser:]: unrecognized selector sent to instance "
///////////////////////////Button save
#IBAction func btnConfirm(_ sender: UIButton)
{
save()
let story = UIStoryboard(name: "Main", bundle: nil)
let controller = story.instantiateViewController(withIdentifier: "ViewController") as! ViewController
controller.modalPresentationStyle = .fullScreen
self.present(controller, animated: false, completion: nil)
}
///////////////////////////function save
func save()
{
let strEingabe = tfUser.text
let defaults = UserDefaults.standard
defaults.set(strEingabe, forKey: "customTextUser")
defaults.synchronize()
let strtest = defaults.string(forKey: "customTextUser")
print(strtest)
}
Thanks a lot
Hi guys,
I've tried a few more things, but without success
Here is the code of my second ViewControll. If I don't enter anything in the "tfUser" text field, everything works as desired and the first view controller is opened and the code of the first view is processed. However, if you enter something in "tfUser", the value is saved, the first ViewController is opened and then crashes, although the same code is processed as if you left "tfUser" empty
i don't know why its working when "tfUser" is empty to and string in "tfUser".
import UIKit
class ViewControllerUser: UIViewController {
#IBOutlet weak var tfUser: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//Button abort
#IBAction func btnBack(_ sender: UIButton)
{
tfUser.text = ""
let vc = storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.modalPresentationStyle = .fullScreen
present(vc, animated: false)
}
//Button confirm
#IBAction func btnConfirm(_ sender: UIButton)
{
save()
let vc = storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.modalPresentationStyle = .fullScreen
present(vc, animated: false)
}
//function save
func save()
{
let strEingabe = ("\(tfUser.text ?? "")")
let defaults = UserDefaults.standard
defaults.set(strEingabe, forKey: "customTextUser")
tfUser.text = ""
}
}

View Controller not loading via instantiateViewController function even with correct identifier

Goal: In a separate storyboard that is loaded via a storyboard reference in the main.storyboard, in a pageViewController acting as the initial view controller, I want to initialize an array object of viewControllers via the function .instantiateViewController(identifier:).
Issue: The last viewController I'm trying to instantiate as a constant is not loading. The error - *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load the scene view controller for identifier 'FinalVC''"
All other viewControllers in this storyboard load fine. This last view controller has a correct custom class linked and a unique storyboard identifier.
Debugging: I've created a breakpoint where this view controller is instantiated and noticed in the debugging console all other view controller objects load as "BillyCues.repeatViewController + unique identification number" while this last vc loads as "UIViewController + 0x000000000000000". It's almost as if this vc is not a part of the app bundle or referenced correctly but it's there when I search in the directory.
Debugging console screen
Things I've tried that did not work:
Check to see if another vc has the same identifier
Clean the build folder
Check "Use Storyboard ID" in the identity inspector
let finalVC = storyBoard.instantiateViewController(identifier: "FinalVC") as! FinalViewController
Restart Xcode
Create a brand new view controller with a different storyboard identifier using the same custom class
Removed all connections from buttons and labels in the last vc
Made sure all storyboard references in main.storyboard has the correct storyboard linked
Conclusion: All my googling has led to other developers encountering the error about NIBs or tableviews not necessarily a view controller. If my vc has a correct custom class and unique identifier the error should not occur. If anyone can offer guidance I'd appreciate it; I'm dumbfounded.
I hope I've asked for help in an appropriate structure but please let me know if more code or screenshots are needed.
PageViewController Code
import UIKit
class LauncherViewController: UIPageViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.setViewControllers([viewControllerList[0]], direction: .forward, animated: false, completion: nil)
// Do any additional setup after loading the view.
}
private var viewControllerList: [UIViewController] = {
let storyBoard = UIStoryboard.cueCreation
let firstVC = storyBoard.instantiateViewController(identifier: "CueNameVC")
let secondVC = storyBoard.instantiateViewController(identifier: "DueDateVC")
let thirdVC = storyBoard.instantiateViewController(identifier: "IconVC")
let fourthVC = storyBoard.instantiateViewController(identifier: "IconColorVC")
let fifthVC = storyBoard.instantiateViewController(identifier: "RepeatVC")
let finalVC = storyBoard.instantiateViewController(identifier: "FinalVC") as! FinalViewController
return [firstVC, secondVC, thirdVC, fourthVC, fifthVC, finalVC]
}()
var selectedReminderBill: CueObject?
public var currentIndex = 0
static var cueName: String = ""
static var cueDate: Date = Date()
static var cueIcon: Data = Data()
static var iconColor:String = "14CC7F"
static var repeatMonthly: Bool = false
// Navigation button functions below to move to the next or previous page
func pushNext() {
if currentIndex + 1 < viewControllerList.count {
self.setViewControllers([self.viewControllerList[self.currentIndex + 1]], direction: .forward, animated: true, completion: nil)
currentIndex += 1
}
}
func pullBack() {
print(currentIndex)
if currentIndex - 1 < viewControllerList.count {
self.setViewControllers([self.viewControllerList[self.currentIndex-1]], direction: .reverse, animated: true, completion: nil)
currentIndex -= 1
}
}
}
FinalViewController Code
import UIKit
import UserNotifications
import RealmSwift
class FinalViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
cueName.text = LauncherViewController.cueName
dueDate.text = CueLogic.convertPaymentDateToString(for: LauncherViewController.cueDate)
iconBackgroundView.backgroundColor = colorLogic.colorWithHexString(hexString: LauncherViewController.iconColor)
cueIcon.image = UIImage(data: LauncherViewController.cueIcon)
repeatsMonthly.text = repeatMonthlyToString
}
override func viewDidLoad() {
super.viewDidLoad()
cueName.layer.cornerRadius = 15
cueName.clipsToBounds = true
iconBackgroundView.layer.cornerRadius = 20
iconBackgroundView.clipsToBounds = true
dueDate.layer.cornerRadius = 15
dueDate.clipsToBounds = true
repeatsMonthly.layer.cornerRadius = 15
repeatsMonthly.clipsToBounds = true
backButton.layer.cornerRadius = 15
backButton.clipsToBounds = true
saveButton.layer.cornerRadius = 15
saveButton.clipsToBounds = true
// Do any additional setup after loading the view.
}
let colorLogic = ColorLogic()
let realm = try! Realm()
weak var delegate: HomeScreenDelegate?
var launcher = LauncherViewController()
var repeatMonthlyToString: String {
get {
if LauncherViewController.repeatMonthly == true {
return "Repeats Monthly: Yes"
} else {
return "Repeats Monthly: No"
}
}
}
#IBOutlet var cueName: UILabel!
#IBOutlet var dueDate: UILabel!
#IBOutlet var saveButton: UIButton!
#IBOutlet var backButton: UIButton!
#IBOutlet var iconBackgroundView: UIView!
#IBOutlet var cueIcon: UIImageView!
#IBOutlet var repeatsMonthly: UILabel!
#IBAction func dismissButtonTapped(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
#IBAction func backButtonTapped(_ sender: Any) {
if let pageController = parent as? LauncherViewController {
pageController.pullBack()
}
}
#IBAction func saveButtonTapped(_ sender: Any) {
// Request authorization from the user to allow notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound], completionHandler: {success, error in
if success {
// schedule test
} else if let error = error {
print("error occured \(error)")
}
})
let newCue = CueObject()
let launcherVC = LauncherViewController.self
newCue.name = launcherVC.cueName
newCue.paymentDate = launcherVC.cueDate
newCue.icon = launcherVC.cueIcon
newCue.iconColor = launcherVC.iconColor
newCue.repeatsMonthly = launcherVC.repeatMonthly
NotificationLogic.scheduleLocalAlertForBill(named: newCue.name, due: newCue.paymentDate, repeatsMonthly: newCue.repeatsMonthly)
saveToDB(for: newCue)
delegate?.loadCuesFromRealm()
self.dismiss(animated: true, completion: nil)
}
func saveToDB(for cue: CueObject) {
do {
try realm.write({
realm.add(cue)
})
} catch {
print("Error - \(error)")
}
}
}
protocol HomeScreenDelegate: AnyObject {
func loadCuesFromRealm()
}
Extension I wrote in another viewController
extension UIStoryboard {
static let onboarding = UIStoryboard(name: "Onboarding", bundle: nil)
static let main = UIStoryboard(name: "Main", bundle: nil)
static let cueCreation = UIStoryboard(name:"CueCreation", bundle: nil)
}
Identity Inspector
Main Storyboard References
I'd do a few things as part of cleanup to start debugging the actual issue. In the storyboard extension, I'd rather use a static function to reference the view controller.
extension UIStoryboard {
class func createFinalVC() -> FinalViewController? {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "FinalVC") as? FinalViewController)
}
}
And for implementing it, I'd use in the view controller presenting FinalViewController:
private func createCreateFinalVC() -> FinalViewController? {
return UIStoryboard.createFinalVC()
}
And finally pushing it into the view,
if let finalVC = createCreateFinalVC() {
yourNavController?.pushViewController(finalVC, animated: true)
}
Solution
I began a process of elimination and started to comment out all of the code in my FinalVC class. I learned that this line of code var launcher = LauncherViewController() was triggering the crash.
Given my limited beginner knowledge I don't know why this would cause a crash; I can only assume that Xcode was trying to initialize two LauncherViewControllers with identical identifier numbers or something along those lines.

SWIFT: Navigate to UIViewControllers from single function with a variable

I'm trying to make a function for my navigation without having to type the exact same lines over and over again.
Trying to do so results into an error: "Use of undeclared type 'viewController'"
Am I doing this completely wrong, should I just place these 4 lines on every button just with different identifier and viewController?
func navigateTo(identifier:String, viewController:UIViewController) {
let newVC = storyboard?.instantiateViewController(withIdentifier: identifier) as! viewController
newVC.modalPresentationStyle = .fullScreen
newVC.modalTransitionStyle = .flipHorizontal
self.present(newVC, animated: true, completion: nil)
}
#IBAction func btnOneTapped(_ sender: Any) {
navigateTo("myVC", MyViewController)
}
Make sure you provide correct identifier...below code works perfectly.
Your Error -> Trying to do so results into an error: "Use of undeclared type 'viewController'"
In this there is a typo mistake for ViewController i.e you are trying this - viewController
Also always try to do Optional-Binding instead to force-unwarpping it may cause crash in your app.
func navigateTo(identifier:String, viewController:UIViewController) {
if let newVC = storyboard?.instantiateViewController(withIdentifier: identifier) {
newVC.modalPresentationStyle = .fullScreen
newVC.modalTransitionStyle = .flipHorizontal
self.present(newVC, animated: true, completion: nil)
}
}
#IBAction func confirmBtn(_ sender: UIButton) {
navigateTo(identifier: "myVC", viewController: MyViewController())
}
You are passing an instance of UIViewController and trying to cast the UIViewController to it in the next line. Modify your function to take in ViewController.Type as parameter like this:
func navigateTo<ViewController: UIViewController>(identifier: String, viewController: ViewController.Type) {
let newVC = storyboard?.instantiateViewController(withIdentifier: identifier) as! ViewController
newVC.modalPresentationStyle = .fullScreen
newVC.modalTransitionStyle = .flipHorizontal
self.present(newVC, animated: true, completion: nil)
}
#IBAction func btnOneTapped(_ sender: Any) {
navigateTo(identifier: "myVC", viewController: MyViewController.self)
}
Thank you guys, I've got it working like so:
func navigateToVC(identifier:String, viewController:ViewController.Type) {
let newVC = storyboard?.instantiateViewController(withIdentifier: identifier) as! ViewController
newVC.modalPresentationStyle = .fullScreen
newVC.modalTransitionStyle = .flipHorizontal
self.present(newVC, animated: true, completion: nil)
}
#IBAction func btnOneTapped(_ sender:Any) {
navigateToVC(identifier: "myVC", viewController: MyViewController.self)
}

Swift segue fatal error

In ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
svc.vericik = self.vericik
}
}
#IBAction func gotoView2(sender: AnyObject) {
self.performSegueWithIdentifier("Segue", sender: self)
self.presentViewController(ViewController2(), animated: true, completion: nil)
}
In ViewController2:
var vericik: String!
#IBOutlet weak var VeriYeri: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
VeriYeri.text = vericik
}
When, I click button on ViewController, ViewController2 page comes to screen and I can see segue data which come from ViewController. But after that, an error occurs:
fatal error: unexpectedly found nil while unwrapping an Optional value
Where am I doing wrong?
Your problem is this line:
self.presentViewController(ViewController2(), animated: true, completion: nil)
This line of code is creating a second ViewController2. The first one was created for you when you did self.performSegueWithIdentifier("Segue", sender: self). This second ViewController2 never gets initialized, so its vericik property is still nil when viewDidLoad runs and implicitly unwraps the optional with VeriYeri.text = vericik (because vericik is declared as String!).
To fix the problem, simply delete this line of code:
self.presentViewController(ViewController2(), animated: true, completion: nil)
It is not needed. The segue creates ViewController2 for you, and you initialize it in prepareForSegue, and then the segue presents ViewController2. There's absolutely no need for you to call presentViewController when using segues.
Somehow, you are setting your vericik variable to nil. Can you print that value before you try setting it before the segue? If it's nil, don't set it as the text, because that's causing the crash.
You need to check that your string is not nil. Try this.
ViewController2
override func viewDidLoad() {
super.viewDidLoad()
if vericik != nil {
//String is not nil, set textfield/label whatever
VeriYeri.text = vericik
}
}
ViewController
if (segue.identifier == "Segue") {
var svc = segue.destinationViewController as! ViewController2;
if self.vericik != nil {
//String is not nil. All is good :)
svc.vericik = self.vericik
} else {
//String is nil, do something...maybe set default text
svc.vericik = "some text"
}
}

Protocols and Delegates in Swift

I have two View Controllers: "DiscoverViewController" and "LocationRequestModalViewController".
The first time a user opens the "DiscoverViewController", I overlay "LocationRequestModalViewController" which contains a little blurb about accessing the users location data and how it can help them.
On the "LocationRequestModalViewController" there are two buttons: "No thanks" and "Use location". I need to send the response from the user back to the "DiscoverViewController"
I have done some research and found that delegates/protocols are the best way to do it, so I followed a guide to get that working, but I'm left with 2 errors and can't figure them out.
The errors are:
On DiscoverViewController
'DiscoverViewController' is not convertible to 'LocationRequestModalViewController'
On LocationRequestModalViewController
'LocationRequestModalViewController' does not have a member name 'sendBackUserLocationDataChoice'
I've marked where the errors are happen in the following files:
DiscoverViewController.swift
class DiscoverViewController: UIViewController, UITextFieldDelegate, CLLocationManagerDelegate, LocationRequestModalViewControllerDelegate {
func showLocationRequestModal() {
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var locationRequestVC: AnyObject! = storyboard.instantiateViewControllerWithIdentifier("locationRequestVC")
self.presentingViewController?.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
self.tabBarController?.presentViewController(locationRequestVC as UIViewController, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
}
func sendBackUserLocationDataChoice(controller: LocationRequestModalViewController, useData: Bool) {
var enableData = useData
controller.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
showLocationRequestModal()
}
}
LocationRequestModalViewController
protocol LocationRequestModalViewControllerDelegate {
func sendBackUserLocationDataChoice(controller:LocationRequestModalViewController,useData:Bool)
}
class LocationRequestModalViewController: UIViewController {
var delegate:LocationRequestModalViewController? = nil
#IBAction func dontUseLocationData(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func useLocationData(sender: AnyObject) {
delegate?.sendBackUserLocationDataChoice(self, useData: true) // This is where error #2 happens
}
override func viewDidLoad() {
super.viewDidLoad()
//Modal appearance stuff here...
}
}
The answer is in your question itself. Both errors tells the exact reason.
Issue 1
let vc = segue.destinationViewController as LocationRequestModalViewController
vc.delegate = self //This is where error 1 happens
The self is of type DiscoverViewController
But you declared the delegate as:
var delegate:LocationRequestModalViewController? = nil
You need to change that to:
var delegate:DiscoverViewController? = nil
Issue 2
The same reason, LocationRequestModalViewController does not confirm to the LocationRequestModalViewControllerDelegate, change the delegate declaration.
You have defined your delegate as having type LocationRequestModalViewController which does not conform to LocationRequestModalViewControllerDelegate.

Resources