I have the following schema:
The controller in the upper right corner is SelectAlbumVC
The controller in the lower left corner is AddPhotoVC
In SelectAlbumVC i have this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = segue.destination as? AddPhotoPostVC
else { fatalError("unexpected view controller for segue") }
guard let cell = sender as? AlbumListCells else { fatalError("unexpected cell for segue") }
switch SegueIdentifier(rawValue: segue.identifier!)! {
case .showAllPhotos:
destination.fetchResult = allPhotos
destination.headerTitleBtnString = cell.allPhotoTitle.text!
case .showCollection:
// get the asset collection for the selected row
let indexPath = tableView.indexPath(for: cell)!
let collection: PHCollection
switch Section(rawValue: indexPath.section)! {
case .smartAlbums:
collection = smartAlbums.object(at: indexPath.row)
case .userCollections:
collection = userCollections.object(at: indexPath.row)
default: return // not reached; all photos section already handled by other segue
}
// configure the view controller with the asset collection
guard let assetCollection = collection as? PHAssetCollection
else { fatalError("expected asset collection") }
destination.fetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
destination.assetCollection = assetCollection
destination.headerTitleBtnString = cell.collectionTitle.text!
destination.isComingFromSelectAlbum = true
}
}
so basically when i click on a cell the segue will be executed and the data passed to AddPhotoVC.
My issue is that when the segue is executed the navigation controller associated with SelectAlbumVC is not dismissed and so when clicking the dismissing button on the AddPhotoVC SelectAlbumVC is presented again (it's the last controller in the stack).
Is there a way to dismiss the navigation controller when the prepare for segue is called?
I've tried to add the the bottom
self.navigationController?.popToRootViewController(animated: false)
but it does not work.
Any help will be really appreciated.
Thank you!
If I understand your code correctly, you're adding another segue back to AddPhotoVC. So if while your app was running and you clicked the "Debug View Hierarchy" (down by the debugger controls in Xcode) you'd see that you now have another AddPhotoVC on top of the original one.
Instead of performing a segue from SelectPhotoVC to AddPhotoVC you may want to consider performing an Unwind Segue instead. This way you could pass the values you want and all the previous VCs would be dismissed.
You are using (multiple) two UINavigationController. That means you are presenting the second screen instead of pushing it.
Now, you mentioned that popToRootViewController does not work, that's because again the two screens have two different UINavigationController. You should dismiss the second screen rather than popping it, because you presented it.
Know the different between pushing/show and presenting viewControllers.
Push/Show --- Pop.
Present --- Dismiss.
Instead of this self.navigationController?.popToRootViewController(animated: false)
Use this:
self.navigationController?.dismiss(animated: true, completion: {
// completion, do something or make it nil.
})
Related
I created a table view and from there let say a user pressed a cell it will go to ListTavleView but the only problem right now is that whenever a user is in ListTableView there is not back button even thought i already embed a navigation controller
and i want the fist view navigation bar is small title second view navigation bar is large title
enter image description here
Below is my code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
controller.item = items
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Below is my storybord setup
Navigation bar with no back button
From the image it seems that view controller is added as a child view controller in current view controller.
There is not need to embedded navigation controller when a cell is pressed becoz there is already a navigation controller at start point so no need to create a new one.(If you present a view controller then you may need to embed navigation controller.)
So the solution is...
Delete the navigation controller.
Connect directly to the destination view controller without navigation controller as there is already.
it is better if you use pushViewController, just get a reference of the other view controller, it will always a back button since you are pushing threw navigation Controller here is a simple example:
let story = UIStoryboard(name: "Main", bundle: nil)
let vc = story.instantiateViewController(withIdentifier: "ExampleViewController") as! ExampleViewController
self.navigationController?.pushViewController(vc, animated: true)
as for the back button, the issue is with your hierarchy.
are you changing the left item of navigation bar in another view controller that might affect navigation bar in your destination view controller.
You are pushing new NavigationController(say Nav.B) to the existing one(Nav.A).
Each navigation controller keeps different navigation stack. The back button is visible when you add viewcontroller to Navigation controller. Read more about UINavigationController.
For your current scenario, you could delete the second navigation controller(i think it not essential) & connect direct segue to ListTableViewController
So this
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
becomes
let controller = segue.destination as! ListTableViewController
When you need large titles(available 11+), you can add this line in viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
And if it needed only for this Viewcontroller, add in viewWillDisappear() or viewDidDisappear()
navigationController?.navigationBar.prefersLargeTitles = false
If you wanted to have navigation bar back button on next view, then just push the target view on navigation, it will show default navigation back button. No, need to any extra work.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
guard let controller = segue.destination as? ListTableViewController else {
return
}
controller.item = items
self.navigationController?.pushViewController(controller, animated: true)
}
}}
And if you are pushing the viewcontroller with segue, then no need to add below line self.navigationController?.pushViewController(controller, animated: true)
I have so strange issue. I have parent VC named NewAdCreationViewController it presents modally child VC named CollectionPicturesViewController.
In child VC I have link to the parent VC.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == picturesCollectionVCSegueName {
let collectionController = (segue.destination as! UINavigationController).viewControllers.first as! CollectionPicturesViewController
let cell = tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? PicturesCollectionTableViewCell
guard cell != nil else{ return }
collectionController.picturesArray = cell!.itemPictures
collectionController.parentVC = self // this is link!
}
}
In child VC I present another VC for picking photos - DKImagePickerController(pod). After picking all my photos appears in collectionView in child VC, and when I tap "Save" I want to pass all that data to parent VC and dismiss child,
#objc func savePictures(){
self.parentVC.pictures = self.picturesArray
self.parentVC.tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
but after I dismiss it my parent reloads completely and starts from viewDidLoad. It presents completely new VC(I've checked in console, it has another address in memory). I really don't know why is that?
On highlighted segue I've changed presentation to Over full screen. And it works perfectly without reloads!
So I'm just learning the basics of Swift. I have a table view of items and when an item is clicked I want to show a view that displays the details of that specific item. I have the below code in which I'm trying to achieve this, although, I'm getting a Bad Instruction runtime error. Any idea where I'm going wrong? Thanks!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
super.prepare(for: segue,sender: sender)
switch(segue.identifier ?? "") {
//the add item button is pressed
case "AddItem":
os_log("Adding a new donation.", log: OSLog.default, type: .debug)
//an existing item is pressed
case "ShowDetailDrill":
guard let itemViewController = segue.destination as? ItemViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedDonationItemCell = sender as? DonationItemCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedDonationItemCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedDonation = donatedItems.getItem(index: indexPath.row)
//TODO: load this to SubmissionVC
itemViewController.donatedItem = selectedDonation
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
Update
I forgot to mention that it breaks here
Update 2: Ok so I removed the navigation controller and changed the segue from a "present modally" to "show". It goes directly from the cell to the item controller. Everything now works, although I'm still somewhat confused on why it wasn't working with the navigation controller. If someone is able to explain the why, I'll mark that as the answer.
is the ShowDetailDrill segue's origin from the tableviewcell? if yes, try deleting it and then creating a new segue from the tableviewcontroller to ItemViewController instead.
I followed Ray Wenderlich tutorial on how to create an iOS book animation, now i'm trying to perform some changes to it.
Original project is composed of Navigation Controller, Books View Controller - showing an array of books, by clicking on a book you can open it - and Book View Controller - showing the selected book open.
What i've added: a View Controller and set it as initial VC; a UIButtonwhich perform a show segue to Navigation Controller.
I want to show View Controller in background after Books View Controller appears but apparently iOS removes the view controllers underneath it from the view hierarchy, this leading to a black background if i set clearColor in the Attributes inspector. I've then added the following code to ViewController.swift to have a transparent background.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = self.storyboard!.instantiateViewControllerWithIdentifier("BooksViewController") as! BooksViewController
vc.view.backgroundColor = UIColor.clearColor()
vc.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
self.presentViewController(vc, animated: true, completion: nil)
}
It works well and i can see the initial VC in the background while seeing the array of books, but i can no longer perform segue to Book View Controller by clicking on a book. So apparently openBook in BooksViewController is never called:
func selectedCell() -> BookCoverCell? {
if let indexPath = collectionView?.indexPathForItemAtPoint(CGPointMake(collectionView!.contentOffset.x + collectionView!.bounds.width / 2, collectionView!.bounds.height / 2)) {
if let cell = collectionView?.cellForItemAtIndexPath(indexPath) as? BookCoverCell {
return cell
}
}
return nil
}
func openBook(book: Book?) {
let vc = storyboard?.instantiateViewControllerWithIdentifier("BookViewController") as! BookViewController
vc.book = selectedCell()?.book
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.navigationController?.pushViewController(vc, animated: true)
return
})
}
I cannot understand where the problem is, any help is really appreciated. Please be gentle, i'm still learning Swift and english is not my native language.
You are using prepareForSegue incorrectly. You do not want to present a view controller in your prepareForSegue. Use segue.destinationViewController as! YourViewController to reference it instead
I have a View controller with an embedded Container View plus a controller
The Container View hosts a UIPageViewController
The View controller has a button, if its clicked I want to update a label in the current displayed page managed by the UIPageView Controller
I am getting the ContainerView Controller with this approach
#IBAction func sendButtonTouched(sender: AnyObject) {
if let vc = self.childViewControllers.last as? ContainerViewController{
vc.pageViewController.view.backgroundColor = UIColor.blueColor()
I get the UIPageViewController and set the color but it does not update
also if go deeper into the rabbit hole to get my currently viewed page I am able to get and set all values but my view never updates
what I really want to do is something like this
#IBAction func sendButtonTouched(sender: AnyObject) {
if let vc = self.childViewControllers.last as? ContainerViewController{
vc.pageViewController.view.backgroundColor = UIColor.blueColor()
print("make it blue baby")
if let pageItemController = vc.getCurrentViewController(){
print(pageItemController.indexLabel.text)
pageItemController.message = self.messageTextView.text
pageItemController.messageImage.image = UIImage()
pageItemController.reloadInputViews()
}
}
}
and in ContainerViewController
func getCurrentViewController()-> MIPViewController?
{
print("\(self.pageViewController.viewControllers!.count) view controllers")
if let vc = self.pageViewController.viewControllers!.first as? PageItemViewController
{
if vc.index < mipCount
// must be a MIPViewController
{
return vc as? MIPViewController
}
}
return nil
}
in my console output i see
make it blue baby
1 view controllers
Optional("This is a message of the number 0")
Optional("")
so everything is called but as stated no view ever updates
I am probably missing something really basic here, so thank you for your help
I also checked other questions e.g. Access Container View Controller from Parent iOS
but afaik using the childViewControllers is also valid