Swipe from one ViewController to another with Swift - uiviewcontroller

I'm currently trying to connect different ViewControllers with each other using Swift, but I'm stuck and would appreciate some help in order to proceed.
What I have now is a main view and when you swipe to the right you would enter another view (like Snapchat works). I've done this using the tutorial found here.
Basically I have a ContainerViewController that store two subviews in its view hierarchy. So I can swipe back and forth between Subview 1 and Subview 2 (see illustration for an idea). Subview 2 has a button that 'modally' loads another ViewController. My question is, if it is possible, how could I make it so that when I swipe left in the "modal ViewController" I would swipe back to Subview 1?
Here is my code that controls the transition from Subview 2 (BViewController) and the modal ViewController (InfoVC):
class BViewController: UIViewController, UITabBarDelegate {
#IBOutlet var item: UIBarButtonItem!
var info: InfoVC = InfoVC(nibName: "InfoVC", bundle: nil )
func tabBar( tabBar: UITabBar!, didSelectItem item: UITabBarItem!){
if item.tag == 2{ // we are in new view controller
info.modalTransitionStyle = UIModalTransitionStyle.CrossDissolve
self.presentViewController(info, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("info hash \(info.hashValue)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And this is my ContainerViewController class where the ScrollView/swipe mechanism is made.
class ContainerViewController: UIViewController {
// Outlet used in storyboard
#IBOutlet var scrollView: UIScrollView?;
override func viewDidLoad() {
super.viewDidLoad();
// 1) Create the three views used in the swipe
var AVc: AViewController = AViewController(nibName: "AViewController", bundle: nil)
var BVc: BViewController = BViewController(nibName: "BViewController", bundle: nil)
// 2) Add in each view to the container view hierarchy
// Add them in opposite order since the view hieracrhy is a stack
self.addChildViewController(BVc);
self.scrollView!.addSubview(BVc.view);
BVc.didMoveToParentViewController(self);
self.addChildViewController(AVc);
self.scrollView!.addSubview(AVc.view);
AVc.didMoveToParentViewController(self);
// 3) Set up the frames of the view controllers to align
// with eachother inside the container view
var adminFrame :CGRect = AVc.view.frame;
adminFrame.origin.x = adminFrame.width;
BVc.view.frame = adminFrame;
var BFrame :CGRect = BVc.view.frame;
BFrame.origin.x = 2*BFrame.width;
// 4) Finally set the size of the scroll view that contains the frames
var scrollWidth: CGFloat = 2 * self.view.frame.width
var scrollHeight: CGFloat = self.view.frame.size.height
self.scrollView!.contentSize = CGSizeMake(scrollWidth, scrollHeight);
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In summary:
What I can do already is to scroll from AViewController to BViewController (back and forth) and go from BViewController to InfoVC by pressing the tab bar item/button. What I then cannot figure out is to swipe backwards in InfoVC and reach AViewController with the same transition as when I swipe between AViewController and BViewController.

You need to add a UISwipeGestureRecognizer to your InfoVC. The easiest way, in Interface Builder, is to drag one from your Object Library onto your InfoVC. You'll see a new icon for it appear in the toolbar at the top of your IB scene. Select it, then in the Attributes Inspector, choose which direction you want the swipe to go--in your case, it's probably left to right.
Next, implement touchesBegan() and touchesEnded() methods in your InfoVC, and use the locations of the events to determine whether the user has swiped whatever distance you specify between the start and end points. If touchesEnded() is at least that distance away, then you can dismiss your InfoVC by removing it from its parent, BViewController.

Load the ViewController from your storyboard
Embed the BViewController in a UINavigation controller.
That will allow you to do all segues in a storyboard kind of fashion
// lets try to load them from the storyboard
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let navigationController = storyboard.instantiateViewControllerWithIdentifier("navController") as UINavigationController
var BVc = navigationController
This will be a good first step. In your BVc prepare for Segue enabling and disabling the scrolling as it will interfere with the containerviews swiping. ReEnable it in your BVc's view did appear.
var parentVC = self.navigationController?.parentViewController as ContainerViewController
parentVC.scrollView?.scrollEnabled = false
You might have to do some additional work to re-receive swipe events in your modal, I am not sure.

I suggest using UIPageViewController, it was created for tasks like this and you can hide the dots bar by deleting these methods:
presentationCountForPageViewController
presentationIndexForPageViewController
For the button to work you need to add:
func pageViewController(pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [AnyObject], transitionCompleted completed: Bool) {
Here is a good tutorial:
https://www.youtube.com/watch?v=8bltsDG2ENQ

Related

Uiview scroll with webview

I have a webpage in WkWebview, and in the webpage there is rect region where I want to put a UIView playing live movie. I've made the webview and uiview, but I cannot figure out how to make the uiview's position to the rect region. That's to say, when user scroll the webview, the uiview scroll also scrolls. It seems the uiview is a a part of webpage. How can I fix the uiview to webpage in webview?
Thanks for any help.
For this can you go to storyboard and see if the uiview is inside the view or not and if then drag it outside and try to set its frame in property console because if you drag and drop to set uiview frame it will be gone in subview of your uiwebview
Hope it will help you
Finally I solved this by use javascript to get the regin location of webpage. Then set the UIView to that location.
Here is the code, in the webpage the regin div has tag movie, and the uiview has name player.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
self.wk.evaluateJavaScript("var rect = document.getElementById('movie').getBoundingClientRect();[rect.left, rect.top];") {
(result, error) -> Void in
print(result)
self.player.view.frame.origin.x = (result as! Array)[0]
self.player.view.frame.origin.y = (result as! Array)[1]
}
}

error passing data to tableController

I am new to swift, and I am trying to make a lobby screen with a text field and a button to move on to the next screen. I connected it in segue to "show" the next view controller.
I need it to check my input field and only continue if the field is not empty, and of course to pass the name to the next view controller, also I cannot manage to show and hide my keyboard (stays on when segueing to the second view controller). Does anyone know how to make a button check the field and only then move to the next view controller?
here is my code(mostly auto generated):
import UIKit
class LobbyController: UIViewController {
#IBOutlet weak var playerNameField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
playerNameField.becomeFirstResponder()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func nameLabel(sender: UITextField) {
playerNameField.becomeFirstResponder()
}
#IBAction func startGameButton(sender: AnyObject) {
self.playerNameField.resignFirstResponder()
}
}
thanks!
If you want to do some input validation before segueing, you should not connect the segue directly to the button, but segue from code.
First you should control click on your button and remove the segue action created.
Now you should create the segue from the view controller, by control dragging from the yellow view controller icon in the document outline.
Then, click on the segue in the storyboard and open its attributes inspector and add a segue identifier.
Now you can perform this segue in code.
So for the IBAction of your button press you can say something like:
#IBAction func startGameButton(sender: AnyObject) {
if self.playerNameField.text?.characters.count != 0{
self.playerNameField.resignFirstResponder()
self.performSegueWithIdentifier("MySegueId", sender: self.playerNameField.text!)
}
}

Animate UINavigationBar when pushing view controller onto stack

The Apple Music app has a nice transition when a view controller with a visible navigation bar ("My Music" view controller) has a view controller with a transparent navigation bar (An artist's view controller) pushed on to the stack.
I'm looking to recreate this transition myself.
I've set the navigationbar to be transparent with the following code:
private func _setNavigationBarVisible(isVisible isVisible: Bool)
{
title = nil
/*
Update the navigation bar's visibility.
Create a helper method to prevent running the same code twice.
*/
func _updateNavigationBarVisibility(isVisible isVisible: Bool, userViewController: UserViewController?)
{
// Create a dummy navigation bar we can rip the default values out of if it should be visible
let navigationBar = UINavigationBar()
let backgroundImage = isVisible ? navigationBar.backgroundImageForBarMetrics(.Default) : UIImage()
let shadowImage = isVisible ? navigationBar.shadowImage : UIImage()
userViewController?.navigationController?.navigationBar.setBackgroundImage(backgroundImage,
forBarMetrics: .Default)
userViewController?.navigationController?.navigationBar.shadowImage = shadowImage
}
// Animate alongside the view controller's presentation transition if there is one
let isTransitionAnimationRun = transitionCoordinator()?.animateAlongsideTransition({ [weak self] context in
_updateNavigationBarVisibility(isVisible: isVisible, userViewController: self)
}, completion: nil)
// Or just update the values if there's no transition
if isTransitionAnimationRun == false
{
_updateNavigationBarVisibility(isVisible: isVisible, userViewController: self)
}
}
This function is run in my viewWillAppear: and viewWillDisappear: methods passing false and true to the isVisible parameter respectively.
This code does achieve a similar effect, but despite the code doing what it should, I want a different effect.
Currently, this code animates the shadow image making them visible on both view controllers rather than like Apple Music where only the one that presented it should have the shadow. As for the background image, there's a visible "swipe" animation as the navigation bar's background animates from transparent to translucent.
There's also an issue with the swipe gesture which I am looking in to, but currently if during the swipe gesture you keep your finger in one position so no swiping occurs and then lift the finger, the navigationbar updates as though the swipe finished, but the view controllers become glitched and the stack is never pushed or popped despite using the fully useable navigation bar.

tvOS - catch MENU button presses during push-view-controller transition

I have a UIViewController in my tvOS app which will only appear for a few seconds, and needs totally customizable MENU button handling. I create it like so:
- (void)viewDidLoad
{
[super viewDidLoad];
// Add a tap gesture recognizer to handle MENU presses.
UITapGestureRecognizer *tapGestureRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
tapGestureRec.allowedPressTypes = #[#(UIPressTypeMenu)];
[self.view addGestureRecognizer:tapGestureRec];
}
- (void)handleTap:(UITapGestureRecognizer *)sender
{
// Code to process the MENU button is here.
}
I display the view controller using pushViewController:animated::
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:identifier];
[self pushViewController:controller animated:isAnimated];
I've found that if the user presses MENU as soon as the screen starts to appear, while the cross-fade transition effect is still displaying, they are able to dodge the UITapGestureRecognizer and go back to a previous screen, which is not intended. They can also cause issues by mashing MENU over and over again--eventually they will escape out of things that they should not be able to.
How can I ensure that the MENU press always reaches my override? Is there a way to specify an app-encompassing MENU button handler and still use a UINavigationController?
A solution that worked for me was to install a UITapGestureRecognizer onto self.navigationController.view. Any taps which get missed by the regular UIViewControllers end up getting caught by the navigation controller's recognizer instead. If you install UITapGestureRecognizers on each view controller you create, the only taps which will fall through the cracks to the navigation controller are taps that occur mid-transition, and it is safe to just ignore those taps completely.
Note that when you want MENU to go back to the home screen, you will need to temporarily remove this tap recognizer and let tvOS handle the tap on its own, as I could find no clean way to escape to the home screen in my code (short of exit(0)).
I have very similar problem because of this behaviour.
In my case I have custom focus management depending on MENU button click detected on pressesEnded and managed by preferredFocusedView. But when navigation controller is poping ViewController and user at this time make second click on MENU button, then UINavigationController class detect pressesEnded and then call preferredFocusedView on my destination ViewController where is my management but pressesEnded will be not called, because was "stolen" by UINavigationController.
Solution for this problem in my case is to create "dummy" class that will be used by UINavigationController like this:
class MenuPhysicalHackNavigationController: UINavigationController {
override func pressesBegan(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
}
override func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
}
override func pressesCancelled(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
}
override func pressesChanged(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
}
}

Need right direction for reload application by action

My scenario is a UIView Controller displays different subview programmatically... Like a game board. Now I'm in a incredible situation:
I can't find a way to reload application after the game is finish!? What I would like to do is after terminate scenario clic on IBAction for star new game. My doubt is about ViewDidLoad or call Main.
try moving your code from viewDidLoad into a new function like -(void)startGame or -(void)refreshViews: and call that function whenever you want to reload your view. even in viewDidLoad you can load your view using this function.
-(void)viewDidLoad
{
[super viewDidLoad];
[self startGame];
}
remember if you are creating allocating and add some of your subviews outside this function then they will remain visible untill and unless yu remove them from superView before calling startGame.
I solved it with this code:
(IBAction)newGame:(id)sender{
UIView *parent = self.view.superview;
[self.view removeFromSuperview];
self.view = nil; // unloads the view
[parent addSubview:self.view];
}