I have added a pop up to my app and it lays over the main parent view controller. But when I swipe on the pop up the main view controller is recognizing the swipes. How can I prioritize the child view controller for actions or stop the gesture recognizer on the main view controller while the pop up is open?
Here's the code where I add the child view:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedItem = self.menuItems[indexPath.row]
let ratingVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "rating") as! RatingView
ratingVC.mainView = self
ratingVC.foodName = selectedItem
self.addChildViewController(ratingVC)
ratingVC.view.frame = self.view.frame
self.view.addSubview(ratingVC.view)
ratingVC.didMove(toParentViewController: self)
}
Thanks for adding the code! It looks like we should change how we are presenting the View Controller. First, I'll explain the simple reason this code doesn't work: When this method returns, the ratingVC variable is cleared, and since it is the only reference to your View Controller, that View Controller is cleared from memory. So your touches cannot be sent to it :-)
There are a couple better ways to present a view controller. Your code is doing what is called "View Controller Containment", and it may be overkill unless you know why you are doing it that way.
The first thing you could do is set the presented view controller as a strong property on this table view controller, and then use presentViewController:animated:completion: to present it. This is simple, and Apple's libraries handle putting it's view on screen.
The recommended way these days, though, to present a View Controller's scene after a table view cell is tapped goes like this:
Use Storyboards to setup both the table view (with prototype cells) and the destination scene.
Ctrl-Drag from the cell prototype to the destination scene.
In your table view controller's code, use prepareForSegue:sender: to give the destination View Controller it's required state.
As a side note, ratingVC.mainView = self in your code may imply another anti-pattern. The easy advice is: Make darn sure that mainView is marked as weak. The better advice is to use a loose-coupling design pattern like delegation, target/action, or callbacks.
Related
An UITableViewController pretty much takes up the entire view. I need a way to limit its height, width and add some shadows etc. For a clear explanation, I won't show the UITableViewController's contents.
Without the use of a storyboard, I subviewed the UITableViewController:
// In another UIViewController
let otherController = OtherController() // A subclass of UITableViewController
let otherControllerView = otherController.view
someView.addSubView(otherControllerView)
[...] // bunch of constraints
Notes:
In AppDelegate, if I set the rootController as OtherController(), everything works as it should. If I change it back to SomeView(), I see my modified tableView. If I should click it, it disappears.
This was the only thing that came close to my issue but sadly, I could not understand the answers provided as nothing made any sense to me.
I need to understand, why it disappears when touched etc.
view.bringSubviewToFront(...) proved futile. I'm gessing that a tableView should be rendered in its own controller and not in another view?
So just to answer this question, indeed you got two options. One is the best way, as suggested by Rakesha. Just use UITableView. Add it as a subview. Done.
And in the future, if you really want any controller to be added onto any UIView, remember you need to add that controller as a child. For example, in your case:
The controller of the view that will hold your UITableViewController will add such UITableViewController as a child.
self.addChild(yourUITableViewController)
self.whatEverViewContainer.addSubview(yourUITableViewController.view)
// Take note of the view of your tableViewController above^.
// Then setup the constraints of your yourUITableViewController.view below.
I hope this helps!
You must add the instance of UITableViewController's subclass as child view controller of the other view controller. You need to ensure few points in order to make it work. The points are as listed below:
Create the instance of your TableViewController
Add it as a child view controller of the other view controller
Add its view as a subview of the desired view (you may do these steps in viewDidLaod since they need to be done only once)
Keeping in mind the view cycle of a view controller. You must keep a weak reference of the child view controller aka TableViewController to adjust its view frame after the parent view controller has laid its subviews.
Code here:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let vc = TableViewController()
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
childVC = vc
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
childVC?.view.frame = view.frame
}
I am trying to implement a navigation bar whose contents persist between different view controllers. For example, I have the following functionality right now:
Non Persistent Navigation Bar
I have set an imageView as the titleView of the navigation bar.
The titleView of the navigation bar transitions along with the view controller here (the image shows some animations by fading in and out). But I would like it to stay hooked onto the top of every screen without any transitions. This would mean that only the part of the view below the navigation bar would show the transition from one view controller to another.
Is that possible in Swift?
Yea that is possible. What you can do is have a container view controller, which can have your navigation bar along with a content view controller.
Now each time you open a new VC, push the new VC on the containerVC's contentVC.
For ex:
let containerVC = self.parentViewController?.containerViewController()
if let _ = containerVC {
containerVC.pushContentViewController(newViewController)
}
Attaching layout screenshot for more understanding.
So if you check here, the Root Container is the view where you can add your new VC as a child VC.
You can do this by changing your UIViewController hierarchy. For this you'll need three view controllers. First will own your UINavigationBar and UIView where other two UIViewController's views will live.
Let's call one with the navigation bar MasterViewController, and other two—ViewControllerA, ViewControllerB respectively.
Somewhere in MasterViewController instantiate child view controllers and add them to your view controller hierarchy. For simplicity's sake let's do everything in viewDidLoad() but you can do do this anywhere you deem it necessary. You could even load view controllers lazily as user demands them.
final class ViewControllerA: UIViewController { }
final class ViewControllerB: UIViewController { }
final class MasterViewController: UIViewController {
var navigationBar = UINavigationBar()
override func viewDidLoad() {
view.addSubview(navigationBar)
let a = ViewControllerA()
let b = ViewControllerB()
addChildViewController(a)
addChildViewController(b)
view.addSubview(a.view)
// you are ready for transitions from a.view to b.view when necessary
}
}
Now you can do transitions from a.view to b.view (and back) and it will affect nothing in master view (which has the navigationBar).
It is important to note that view hierarchy and view controller hierarchy are not liked in any way and you are responsible for managing both.
I have a table view controller and another view controller. Now my requirement is that i need to swipe the table view controller half over the another view controller when i swipe the view controller. The image i can show is like this:
Is it possible to achieve this by using the Swipegesture . If possible how can i do this in Swift3?
While there are libraries out there to do this for you, if you are going to do this yourself, the basic idea is that you can create a "swipe from edge" gesture recognizer to initiate a custom presentation of a view controller (that has your menu on it). It consists of:
a custom transitioning delegate (conforming to UIViewControllerTransitioningDelegate protocol) that specifies the animation controller and interaction controller (both described below) to be used during the presentation of the next scene;
an animation controller (conforming to UIViewControllerAnimatedTransitioning) that dictates the animation of the scene from the right edge;
an interaction controller (a UIPercentDrivenInteractiveTransition) that you can use to optionally drive the transition via a gesture;
a presentation controller (a UIPresentationController subclass) that dictates whether the presented view will be removed or not (in this case you do not want it removed), as well as any other chrome to be animated alongside the animated presentation of the new scene (e.g. you often dim or blur the presenting view); and
gesture recognizers to drive the interaction controller.
For more information, see WWDC videos Custom Transitions Using View Controllers and A Look Inside Presentation Controllers.
See https://github.com/robertmryan/SwiftCustomTransitions/tree/rightside for a Swift 3 example that renders the following UX:
This is all admittedly complicated enough that you may well want to consider third party libraries for presenting slide-in menus. But if you wanted to "roll your own", these are the basic pieces involved.
What I would do is first create and hide a UIView that lets you select those UIViewController on the right side of the screen and animate it to show when the user swipes.
Then you implement this method that returns UIViewControllerAnimatedTransitioning, a class that you want to implement for custom transitioning.
- (id<UIViewControllerAnimatedTransitioning>)
navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC
This could be a good tutorial for custom transitioning.
https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions
I would look into SWRevealViewController. It gives you the behavior you are looking for in just a few lines of code. You can present an SWRevealViewController that will hold your top and bottom UIViewController and present that as your scene.
// Your Front View Controller
let frontStoryboard = UIStoryboard(name: "FrontStoryboard", bundle: .main)
let frontVC = frontStoryboard.instantiateInitialViewController()
// Your Rear View Controller
let rearStoryboard = UIStoryboard(name: "RearStoryboard", bundle: .main)
let rearVC = rearStoryboard.instantiateInitialViewController()
// Create Reveal View Controller From Both
if let revealVC = SWRevealViewController(rearViewController: rearVC, frontViewController: frontVC) {
present(revealVC, animated: true) {
}
}
Using SWRevealViewController you can set the UIViewController that you are trying to do the sliding in as the frontViewController and you can present the rearViewController simply using a single line of code.
// When you import SWRevealViewController every UIViewController
// has a method revealViewController() that returns the revealViewController
// that you can tell to toggle it's reveal state using the below
self.revealViewController().revealToggle(animated: true)
EDIT
I believe you are trying to build a sliding navigation menu. Here is a link to a Ray Wenderlich tutorial where he is creating exactly the navigation you are looking for.
I am using SWRevealViewController for a slide-out menu in my app. The main View Controller contains a WKWebView to open URLs. When the slide-out button is pressed, a table view Controller appears.
I want to have the name of a website displayed in a Table View Cell in the Table View Controller in the slide-out menu and open that website in the WKWebView in the main View Controller when the cell is pressed.
This may seem like an easy question, but I can't seem to find anything about it online.
Thanks in advance for your help.
As far as I remember the implementation of SWRevealViewController, it uses the static table view.
If you are okay with the static table view that will have your links then you may just create a delegate method that will be called based on the cell that was chosen on the Table View Controller. This delegate will notify your ViewController and say which link to open.
Hope it's clear. Let me know if you need more details.
In SideMenuViewController (view controller in which you have table with links)
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let mainViewController = MainViewController()
mainViewController.link = linksArray[indexPath.row]
self.revealViewController().setFrontViewController(mainViewController, animated: true)
}
init new main view controller that have WKWebView
pass link as a property
set that view controller as new front view controller
I have two UIButton and when you click on each you will go to table view controller and there you can select something.
My question is how to change the button title on selection of any cell.
I want to replace button title with selected cell's name.
There are two ways to pass data back to the previous viewController.
1) Using Delegates, check this
2) Using Notifications, check this
Bind button in your cell as #IBOutlet
When cell selected delegate method tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath. Here you can get your cell with [tableView cellForRowAtIndexPath:indexPath]
Change your button's title [cell.yourButton setTitle...]
If you were to make your navigation controller into you root view controller and put the view controller you wish to access the buttons from as its top view controller then you could do the following when a cell is tapped:
if let topVC = navigationController?.topViewController as? MyButtonViewController {
topVC.button1Name.setTitle("New Title", forState: .Normal)
}
Then you can hide the navigation bar on the top view controller with the following:
override func viewWillAppear(animated: Bool) {
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(animated: Bool) {
navigationController?.setNavigationBarHidden(false, animated: animated)
}
Instead of using two navigation controllers you can use a single NavC and then make your view controller with the two buttons as the root view controller. Then when the buttons are pressed you load the appropriate table view controller. Also if your table view controller has the same data source then you can use a single table view controller itself.
Then depending on which button is pressed you push the appropriate table view controller but in the prepareForSegue keep track of which button was pressed so that you can change its title when a cell is selected in the table view controller.
To pass the selected cell details from the table view controller you can make use of a delegate which your view controller can implement and the table view controller will call and pass the selected cell details.
So in short do this.
Make your navC as the initial view controller.
Make your view controller with the two buttons as the root view controller of the navC
In the IBAction of the two buttons store which button was pressed so that you will know which buttons's title has to be changed.
When you load table view controller set the view controller as a custom delegate.
In the language list table view controller's didSelectRowWithIndexPath use the delegate to pass the selected cell info and pop the view controller.
In the delegate method of the view controller change the button title that you have stored in step 3 to the selected cell's text.
I have another solution which I have uploaded here. It does not contain any custom view controllers except the ViewController that has the two buttons. It makes use of segues and unwind segues.
http://www.filedropper.com/twobuttonnavc