I have seen plenty of questions similar to this - but not quite the same..
So I have a UISplitViewController, and the detail view controller has a lot of heavy drawing in it, so I don't want to reinit it every time I select a new row on the master view. So I observe when the user selects a row, and only perform a segue if it doesn't see a detail view controller.
Here's where the problem lies... On the iPad, if I resize the view via multi-tasking, eventually the UISplitViewController only shows the master. But when I select a row, it thinks the detail view controller is nil and allocates a new detail view controller (performs the segue). The thing is that the detail view controller still exists, It just doesn't show up on the split view controller's childViewControllers. I want to perform a segue when in the compact size, but I don't want it to recreate a new view - I want it to use the view that the split view controller is holding.
Sorry if this seems confusing.
Thanks
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// try to get my custom detail view controller
if let myVC = splitViewController?.detailViewController?.childViewControllers.first as? MyViewController {
let selectedRow = indexPath.row
myVC.doSomething(selectedRow)
} else {
// perform segue if it's not available
performSegue(withIdentifier: "showMyVC", sender: self)
}
}
Related
This error seems common, but I can't seem to find a similar root cause as mine so the solutions aren't working.
I initially had single View Controller (VC 1) in my app, embedded in a Navigation Controller (NC).
I then added a second VC 2 and removed the NC from the VC 1. Then I embedded VC 2 in a NC and made it the initial view controller.
VC 2 is a Table View Controller that, when a cell is tapped:
instantiates VC 1,
alters a variable, and
uses navigationController to push VC 2
VC 2 contains a webView that does most of the work.
After setting all this up, I got a breakpoint and a warning that:
“View Controller“ is unreachable because it has no entry points, and no identifier for runtime access via -[UIStoryboard instantiateViewControllerWithIdentifier:].
I added a new NC back to VC 1 thinking that would fix things, but it didn't. Just changed to
“Navigation Controller“ is unreachable because it has no entry points, and no identifier for runtime access via -[UIStoryboard instantiateViewControllerWithIdentifier:].
Not sure if this is enough information, but is there a potentially clear solution here?
Edit:
Adding the didSelectRowAt function in VC 2 that takes you to VC 1, in case it helps:
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let vc = storyboard?.instantiateViewController(withIdentifier: "Browser") as? ViewController {
vc.selectedWebsite = websites[indexPath.row]
vc.approvedWebsites = websites
navigationController?.pushViewController(vc, animated: true)
}
}
In the Storyboard select the view controller then Identity Inspector in the right pan and set Identity -> Storyboard ID to "Browser"
You have to set the navigation controller as the initial view controller to avoid this error, it wont appear as a "screen" anyway. You can then make the first storyboard that appears for the user by holding control then click and drag from the navigation controller to the screen of your choice.
I’m new to collection views and I’m struggling to get a collection views selected cell to change the content of another collection views displayed content which both collection views are on same ViewController.
I have two working collection views on one view controller and want my selection on first collection view to change the content of the second collection view.
Didn’t feel I need to post my code of collectionViews as I set up the collectionViews like normal, as per Apple documentation.
So both collection views work, just this requested function doesn’t yet.
An example of the data would be:
Collection view one
Let arrayDrinks = [“beer”, “spirit”, “wine”]
Collection view two ( changes depending what is selected in collection view one)
Let arrayBeer = [“Carling”, “fosters”, “Carlsberg”]
Let arraySpirit = [“vodka”, “whiskey”]
Let arrayWine = [“red wine”, “white wine”, rose]
Any help would be great. Thanks
Implement the collection view's delegate method.
extension YourViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if collectionView == firstCollectionView {
// Identify the selected cell here and change in your second collection view accordingly.
}
}
}
And don't forget to set the delegate in your viewcontroller:
firstCollectionView.delegate = self
secondCollectionView.delegate = self
I've implemented a UIPageViewController that allows the user to add/remove/delete new "pages" via a separate UITableView, similar to how Apple implements Cities/Weather Locations in the iPhone Weather app. Project is posted below. There is no weather or fancy UI in it at this point – I'm just trying to focus on the UIPageViewController management below. Hopefully this is useful if anyone wondering how to implement UIPageViewController with pages managed via UITableView:
https://github.com/gallaugher/PageViewControllerDemo
It seems to work fine, but I'm new to this & quite uncertain if I've done this using recommended approaches or if it's "Swifty".
Right now I have:
PageViewController [Initial View Controller]
- Sets delegate & data source to self
- Instantiates first UIViewController (imagine a CityViewController for local weather, even though details & UI not added in this example) & sets initial values.
- Cities (weather locations) are kept in a [String] array: citiesArray
In CityViewController - when "Cities" button is clicked in lower-right (created in interface builder), like Apple Weather, it opens a UITableView (CityListViewController).
- To get to this CityListViewController, I trigger a segue drawn directly via the interface builder from the "Cities" button in CityViewController to the CityListViewController, presenting modally.
- preapareForSegue passes citiesArray to the destination CityListViewController (UITableView).
In CityListViewController (the UITableView)
- User can add cities, move, delete in UITableView updating tableView & array
- Clicking a tableView row (a city's name or "Local Weather") triggers a perform segue, unwinding to CityViewController, getting source CityListViewController and using this to pass data back to the CityViewController (e.g. citiesArray = controller.citiesArray).
- This #IBAction unwind function calls an unwind to the PageViewController (really does nothing more than pass data through from the TableView in CityListViewController to the UIPageViewController PageViewController).
Unwind in PageViewController
- Grabs source (as CityViewController)
- Passes key data back (e.g. citiesArray = controller.citiesArray)
- Calls a function to instantiate view controller for the current page & set PageControl index, etc.
Q1:
While this seems to work & I haven't managed to break it during testing, is it a sound approach to go from UITableView, unwinding to a ViewController that simply triggers another unwind to the UIPageViewController, with nothing done other than pass data through?
Q2:
I've implemented the UIPageControl by building it programmatically in the PageViewController, but the button that segues to the CityListViewController (the UITableView) was created in the CityViewController using Interface Builder. Is this the proper approach? I couldn't seem to get both of these created within the same VC.
Thanks so much for those who had the patience to wade through this convoluted explanation. Still trying to get a handle on data passing among VCs, and how this relates to PageControllers & the TableViews.
For this interested in the solution I've posted to GitHub at:
https://github.com/gallaugher/PageViewControllerDemo
I've posted with more generic names - PageVewController, DetailViewController, and ListViewController, so this is easier to reuse and perhaps follow.
Thanks to Sazan Dauti for answering this question outside of StackOverflow. It is possible to segue directly between the PageController & the ListController that contains a TableView for managing pages an array of similar pages (e.g. like Apple does in the Weather app where you can add/delete/move cities), keeping the Detail (Cities in original example) free of any knowledge that it's in a PageView.
- Drag a segue directly from the UIPageViewController to the UIViewController with the TableView that contains, in my case, a list of locations (not the detail that's managed by the PageView). The segue should present modally. Key variables should be passed to the ListViewController in a prepare for segue in the PageViewController like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ToListViewController" {
let controller = segue.destination as! ListViewController
controller.listArray = listArray
controller.currentPage = currentPage
}
}
Be sure the values passed (listArray & currentPage in the case above) are declared in ListViewController.
Add an Unwind method to the PageViewController similar to this:
#IBAction func unwindFromListViewController(sender: UIStoryboardSegue) {
pageControl.numberOfPages = listArray.count
pageControl.currentPage = currentPage
setViewControllers([createDetailViewController(currentPage)], direction: .forward, animated: false, completion: nil)
}
setViewControllers above refers to the function written in the PageViewController that contains the logic to instantiate the DetailViewController for the current page.
Return segue is created by dragging from a TableViewCell in the ListView to the "Exit' button (far right of the three buttons at the top/title view of this UIViewController). You'll be asked the name for an unwind method that should be in the PageViewController (in the example above I've called it unwindFromListViewController), so be sure to add this method before trying to make the segue.
Add a prepare for segue function to the ListViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ToPageViewController" {
let controller = segue.destination as! PageViewController
controller.currentPage = currentPage
controller.listArray = listArray
}
}
and in tableView didSelectRowAt, trigger the perform segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
currentPage = indexPath.row // set current page to row selected
performSegue(withIdentifier: "ToPageViewController", sender: self)
}
I couldn't find any decent examples online demonstrating how this was done, so hopefully this is understandable and efficient. Any corrections are welcome. Cheers!
I have a CollectionView running in my project and the collectionViewController connected to another tableview using custom segue as follows.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedMenuItem = indexPath.row
//Present new view controller
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main",bundle: nil)
var destViewController : UINavigationController
switch (indexPath.row) {
case 0:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("NewTableView") as UINavigationController
break
default:
destViewController = mainStoryboard.instantiateViewControllerWithIdentifier("NewTableView") as UINavigationController
break
}
self.presentViewController(destViewController, animated: true, completion: nil)
}
above code populating my NewTableview successfully but the tableview missing navigation bar completely.
so far my tries as follow...
i embedded new navigationController to collectionView results remain unchanged.
i embedded new navigationController to NewTableView results remain unchanged.
i tried unwind segue method using custom navigation button action method.so i insert the navigation bar item and button to the view and Method works,but it works strangly when navigation bar button pressed it takes me to my main collection view controller thats what i what.but i have another back button appears in collection view rowindex item name on it.when i pressed the button it take me back to the NewTableView its like looping through.
i don't know what i am missing (any delegate method).and i noticed the newTableView data loading from the bottom with animation...
i wondering is there any way to put a navigation programmatically with back button behaviour...
Thanks in Advance.....
You'll have to instantiate the navigation controller, not the actual view controller from your storyboard. So instead of instantiating NewTableView (the view controller you want embedded in the navigation controller), you should add an identifier to your UINavigationController so that it will be instantiated instead of the view controller itself.
Just imagine that you reuse the NewTableView controller which does not require a navigation controller as its root, how would iOS be aware of that ? So add an identifier to your hosting UINavigationController and instantiate that. The navigation controller has a root dependency on the rootViewController which will be shown by default, so you only need to display the UINavigationController and the NewTableView controller will be shown based on the dependency it has with the navigation controller.
So I want a signature view within a table cell. Obviously whenever somebody tries to draw in the cell, the table scrolls.
How would I stop the scrolling but ONLY when the user is writing in the signature box?
I found better solution for this issue rather than putting button. Implement the delegate methods in viewController,
class mainVC: UIViewController,YPSignatureDelegate {
Than set delegate of signature view to this view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "SignatureCell", for: indexPath) as! SignatureCell
cell.signatureView.delegate = self
return cell
}
And then add these code. This are two delegates of YPSignature. Add in Main view controller
func didStart() {
tableView.isScrollEnabled = false
}
// didFinish() is called rigth after the last touch of a gesture is registered in the view.
// Can be used to enabe scrolling in a scroll view if it has previous been disabled.
func didFinish() {
tableView.isScrollEnabled = true
}
I would solve this with a button covering the cell, and when the user taps it, the cell displays the YPDrawSignatureView. Just before the signature view is shown, disable the scrolling:
tableView.scrollEnabled = false
Later when you save the signature, enable scrolling again by setting scrollEnabled to true.
I added a uitableview and custom cells. In one of the custom cells contain a button(ex. addSignatureButton) on the top of signatureView.
I used delegate method to communicate between uitableviewcell and uiviewcontroller. A delegate is added to UITableViewCell to notify whether the addSignatureButton is tapped. Once it is tapped, addSignatureButton is hidden, signatureView is visible and the tableview's scroll is disabled. When user finishes adding signature, signatureView is hidden, addSignatureButton is visible and tableview scroll is enabled.
https://github.com/alvinvgeorge/DrawSignatureOnTableViewCell