I'm trying to add a FittedSheet (Repo) to my app, and in their Usage explanation, they just have this chunk of code with no further explanation regarding where to put it.
let controller = MyViewController()
let sheetController = SheetViewController(controller: controller)
// It is important to set animated to false or it behaves weird currently
self.present(sheetController, animated: false, completion: nil)
I want to know where that code is supposed to go so I can include the basic version of a FittedSheet in my app. Any input would be greatly appreciated, I'm new to swift and have all the backend data in my app working but am struggling to display it.
MyViewController() is a controller name whose data need to be present using the FittedSheet library.
Let me take an example.
In the below example, i create customSheetController in my project. I design UI in storyboard and create a swift class for same.
customSheetController storyboard UI
Below is customSheetController swift class syntax
class customSheetController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
In some another viewcontroller class, open that sheet on collection view item click.
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let sheetView = storyboard.instantiateViewController(withIdentifier: "customSheetController") as? customSheetController {
let sheetController = SheetViewController(controller: sheetView)
// It is important to set animated to false or it behaves weird currently
self.present(sheetController, animated: false, completion: nil)
}
}
In the above example i opened a sheet on collection view item click, you can add above code on button event or any event from where you want to open that sheet.
another example
Let me take another example, suppose you have two viewcontroller class in your project lets say A and B. You want to present A controller over B controller. Then you need to modify the code as below.
In B controller class, suppose you want to present A controller on button click then you can write below code on the button click event
let controller = A()
let sheetController = SheetViewController(controller: controller)
self.present(sheetController, animated: false, completion: nil)
Related
I have an application with a main UIViewController. When I press a button ("Save") I have a custom popup (UIViewController) present itself. Within this popup I have another button, whereby if I press it I want to dismiss the current popup view controller and then immediately present another different custom popup view controller. I am able to dismiss the first popup , but then i get an error(see below). I am using a protocol to get this working, but I am making a mistake somewhere. Please can some one advise?
[![enter image description here][1]][1]
class mainViewController: UIViewController, popUpDismissedDelegate{
// CUSTOM PROTOCOL DELEGATE FUNCTION
func popUpDimissed() {
// SHOW ANOTHER POPUP TO CREATE CUSTOM HASHTAGS!
let createTagVC = storyboard?.instantiateViewController(withIdentifier: "createTag") as! CreateHashTagPopUpViewController
present(createTagVC, animated: true, completion: nil)
}
// SAVE PDF
#IBAction func savePdf(_ sender: Any) {
// SHOW CUSTOM SELECTION OH HASHTAGS TO ASSIGN PDF
let popUpVC = storyboard?.instantiateViewController(withIdentifier: "hashtagpicker") as! CustomHashTagPopup
popUpVC.delegate = self
present(popUpVC, animated: true, completion: nil)
}
}
protocol popUpDismissedDelegate {
func popUpDimissed()
}
class CustomHashTagPopup: UIViewController, UITableViewDelegate, UITableViewDataSource{
var delegate: popUpDismissedDelegate!
// TAP ON CELLS
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(indexPath == [0,0]){
// OPTION TO CREATE A NEW HASHTAG
self.dismiss(animated: true) {
self.delegate.popUpDimissed()
}
}else{
// DO NOTHING
// TO SELECT A HASH TAG USE NEEDS TO PRESS ON THE CHECKBOX!
}
}
}
ERROR:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
popup - viewDidDisappear
Could not cast value of type 'UIViewController' (0x10c1ec1f0) to
'zapdocuments.CreateHashTagPopUpViewController' (0x107cd0520).
2018-08-28 18:18:48.196815+0100 zapdocuments[28989:4660894] Could not
cast value of type 'UIViewController' (0x10c1ec1f0) to
'zapdocuments.CreateHashTagPopUpViewController' (0x107cd0520).
You didn't actually post the error, but I think I know what the problem is. You're probably trying to present the next VC before the old one actually dismissed.
You should use the completion parameter of the dismiss method and put your delegate callback in there, to ensure you don't try to do anything until it's fully dismissed.
Move your presentation of createTagVC to the next loop of the runloop. This should guarantee that the UI is in the correct "non-presenting" state.
DispatchQueue.main.async {
present(createTagVC, animated: true, completion: nil)
}
Sometimes, some things aren't really done until the runloop ends. Best to start fresh in the next one.
I have an mainViewcontroller in that one button called get value.Then i am calling dataviewcontroller to select any item in collection view cell.Once user select any cell.That particular dataviewcontroller will dismiss and while dismiss it will have the user selected item name and it will display in mainViewcontroller .Now the view controller is not dismissing.
Here the code :
In my mainViewcontroller:
var SelectedName: String? = ""
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
currentTF.text = SelectedName
}
Now dataviewcontroller :
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView.cellForItem(at: indexPath) != nil {
selectedNamesss = allitems[indexPath.row] as String
}
calldismiss()
print(selectedNamesss)
}
func calldismiss() {
if let presenter = presentingViewController as? mainViewcontroller {
presenter.SelectedName = selectedNamesss
}
dismiss(animated: true, completion: nil)
}
Now how can i solve this.My viewcontroller is not dismising and values also not showing.
Thanks in advance ~
My Suggestion is to use "UnwindSegue" technique, which is simply works like "PerformSegue", So you can send back values very easily using prepare for segue and will be back to main controller. Here is tutorial for unwind segue. Or you can find more from google.
For The problem where data is not showing, problem is with this condition:
if let presenter = presentingViewController as? mainViewcontroller {
presenter.SelectedName = selectedNamesss
}
this condition will never be true as the presentingViewController would always be a UINavigationController. so you need to change that may be something like follows:
if let presenter = (presentingViewController as! UINavigationController).viewControllers.first! as? mainViewcontroller {
presenter.SelectedName = name
}
Here i am using viewControllers.first! in condition as mainViewcontroller was at first index of the viewControllers array. Check the viewControllers array and find the index of mainViewcontroller and make changes accordingly.
I made this changes and the code is working for me. I am getting the selected name on the mainViewcontroller.
Also i would like to mention that this is not the right way to transfer data backwards. You can use delegates, blocks or unwind segue to achieve this.
Though this is working i am attaching a gif to show this working.
Regarding the issue where your controller is not being dismissed, i am presenting the controller in following way:
let dataVC = self.storyboard?.instantiateViewController(withIdentifier: "dataviewcontroller") as! dataviewcontroller
self.present(vc, animated: true, completion: nil)
Try this and see if this helps you :)
I´m coding an application using swift3/xcode 8. My application have a central view controller, called MenuViewController, which is responsible for the side menu.
Inside this one, i load the other views as child views, so in this way I can use the side menu in all other views.
But I´m in doubt now: In a specific view controller I have a table view, which have a method to perform an action on selecting an item. This action calls other
view and pass a parameter.
So, how do I load this view in the BaseViewController with this parameter? Is it possible?
Please let me know if it is not clear....
Thank you!
Update 1:
I´m using a solution I found interesting:
Cocoacasts
Basically I have this code on MenuViewController:
Declare the view to be loaded in MenuViewController:
lazy var filamentoViewController: FilamentoViewController = {
let storyboard = storyboard.instantiateViewController(withIdentifier: "FilamentoViewController") as! FilamentoViewController
self.addViewControllerAsChildViewController(childViewController: viewController)
return viewController
}()
Adds the viewcontroller as child in MenuViewController:
private fun addViewControllerAsChildViewController(childViewController: UIViewController)
{
addChildViewController(childViewController)
view.addSubView(childViewController.view)
childViewController.view.frame = view.bounds
childViewController.view.autoResizingMask = [.flexibleWidth, .flexibleHeight]
childViewController.didMove(toParentViewController: self)
}
Code in FilamentoViewController which calls other viewcontroller:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath)
{
let editFilamentoViewController:EditFilamentoViewController = EditFilamentoViewController()
editFilamentoViewController.cod = indexPath.row
self.presentViewController(editFilamentoViewController, animated: true, completion: nil)
}
The code
self.presentViewController(editFilamentoViewController, animated: true, completion: nil)
is the one I want do load into MenuViewController.
Update 2:
MenuViewController is a CentreViewController. I load the other views in the center and the menu comes from left.
There is a built-in controller for your kind of requirement(i.e UISplitViewController) , Have you considered this?
Apple documentation
Sample Tutorial
And there are also slide-out menu libraries.
slide out menu
I'll give an example of what I want so it's not so confusing:
Example:
Let's say that I have a map that adds every time that my user scrolls 3 annotations dynamically. Now I have a button under the map and when I press it I go to another viewController do what I want and get back to the viewController with the map, now I want to find all the annotations that my map had and not reload the view at all.
I used to use this function that I made to move between viewControllers:
func move(identifier: String , viewController : UIViewController) -> Void {
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc: UIViewController = mstoryboard.instantiateViewControllerWithIdentifier(identifier)
viewController.presentViewController(vc, animated: true, completion: nil)
}
I also tried this:
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("view") as? MyViewcontroller
self.presentViewController(vc!, animated: true, completion: nil)
These two when I use them the viewcontroller that appears is calling viewDidload so its like it appeared for the first time.
Another example is the tabBarViewController if you notice when you navigate through tabs nothing reloads (only function that is called is viewDidAppear )
EDIT
test file
The problem is caused by the fact that the map controller gets deallocated when navigating back to the other controller, and another one is created when you want to move again to the map screen.
What you need is to hold on onto the same controller instance, and present that one. Keeping a strong reference in the presenting controller would suffice.
class PresentingController {
// making the property lazy will result in the getter code
// being executed only when asked the first time
lazy var mapController = { () -> UIViewController in
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
return mstoryboard.instantiateViewControllerWithIdentifier("mapControllerIdentifier")
}()
func moveToMap() {
// simply use the mapController property
// the property reference will make sure the controller won't
// get deallocated, so every time you navigate to that screen
// you'll get the same controller
presentViewController(mapController, animated: true, completion: nil)
}
}
According to the same project you posted, you instantiate a new UIViewController when going from view 2 back to view 1 and that is why your viewDidLoad gets called again and your entire map view is reloaded.
In your sample project, instead of
lazy var mapController2 = { () -> UIViewController in
let mstoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
return mstoryboard.instantiateViewController(withIdentifier: "first")
}
You should just dismiss your view 2 on the button press.
#IBAction func butto(_ sender: AnyObject) {
//Your initial code
//PresentingController().moveToMap(self, flag: 1)
self.dismiss(animated: true, completion: nil)
}
When you present a new UIViewController, the older UIViewController is not removed from memory, it is just hidden behind the new UIViewController. So whenever you wish to go back to a UIViewController with the previous state maintained, all you need to do is close the new UIViewController
However, if you are doing some tasks that you performed on your second UIViewController that you wish to be reflected in your initial UIViewController, you will have to setup closures to update your initial UIViewController.
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