I create one UIScrollView with programmatically code and I have one button in my view (not exist in UIScrollView).
when I click on this button go to next page with modal transition. I create Cancel button in next page that when I click on it back to main page (this page have UIScrollView).
I want when click on Cancel button and return to main page call one method that is in main page and in this method change ContentOfSet my ScrollView... but not working!!!!
this is my code:
mainView.m
- (void)viewDidLoad{
UIScrollView *scrollbar = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,width, height)];
scrollbar.directionalLockEnabled = YES;
scrollbar.backgroundColor = [UIColor whiteColor];
scrollbar.maximumZoomScale = 1.0;
scrollbar.minimumZoomScale = 1.0;
scrollbar.clipsToBounds = YES;
scrollbar.showsHorizontalScrollIndicator = YES;
scrollbar.pagingEnabled = YES;
[scrollbar setContentSize:CGSizeMake(scrollbar.frame.size.width * 4,scrollbar.frame.size.height)];
scrollbar.contentOffset = CGPointMake(0, 0);
scrollbar.delegate = self;
[self.view addSubview:scrollbar];
[super viewDidLoad];
}
-(void)ChangeMainScrollContentOffset{
scrollbar.contentOffset = CGPointMake(scrollbar.frame.size.width * (3), 0);
}
this button is in root View :
- (IBAction)AddView:(id)sender{
AddStationController *add = [[AddStationController alloc]initWithNibName:#"AddStationController" bundle:nil];
[add setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.view.window.rootViewController presentViewController:add animated:YES completion:nil];
}
this button is Cancel button for return in my mainView:
- (IBAction)BackView:(id)sender{
mainView *main = [[mainView alloc]init];
[main ChangeMainScrollContentOffset];
[self dismissViewControllerAnimated:YES completion:nil];
}
please guide me about that!!!
I so confused why not working ContentOffSet!!!
I think your problem is that you didn't add ChangeMainScrollContentOffset
method to your ViewController.h file.
Just add it in ViewController.h file and than call it.
You second viewController should have reference to previous view controller.
When you do this:
mainView *main = [[mainView alloc]init];
[main ChangeMainScrollContentOffset];
You create another instance of mainView which not on screen and change contentOffset on ANOTHER scrollView :)
You should add property delegate to your AddStationController
AddStationController.h
#property (nonatomic, weak) mainView* delegate;
mainView:
- (IBAction)AddView:(id)sender{
AddStationController *add = [[AddStationController alloc]initWithNibName:#"AddStationController" bundle:nil];
>> add.delegate = self; <<
[add setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self.view.window.rootViewController presentViewController:add animated:YES completion:nil];
}
Cancel button action
- (IBAction)BackView:(id)sender{
>> [self.delegate ChangeMainScrollContentOffset]; <<
[self dismissViewControllerAnimated:YES completion:nil];
}
Related
I have implemented a niceUIPageViewControl with aPageControl. When swiping the indicator changes and shows the correct current page.
However i noticed that all that it takes for the current page indicator to switch is to start swiping. Meaning if i start swiping but then let go of the finger the current page indicator has switched like the page has been switched however it has not. This is the code that i am using to make the switch:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
NSUInteger index = [self.navigationController.viewControllers indexOfObject:viewController];
self.pageControl.currentPage = index;
}
Another thing i noticed was that when i swipe right and left changing views FAST the page indicator is just stuck and does not move.
So it only works when you are not changing views fast. If you need any additional code let me know. Thank you.
Edit
This is the code i use to implement my UIPageViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
self.navigationItem.title = #"Tabell";
self.identifiers = [[NSMutableArray alloc] init];
[self.identifiers addObject:#"rankTable"];
[self.identifiers addObject:#"page2"];
// Create page view controller
self.pageViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"PageViewController"];
self.pageViewController.dataSource = self;
self.pageViewController.delegate = self;
self.navigationController.delegate = self;
CGSize navBarSize = self.navigationController.navigationBar.bounds.size;
CGPoint origin = CGPointMake( navBarSize.width/2, navBarSize.height/2 );
self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(origin.x, origin.y+16,
0, 0)]; //Here added 45 to Y and it did the trick
self.pageControl.pageIndicatorTintColor = navbarColor;
self.pageControl.currentPageIndicatorTintColor = [UIColor lightGrayColor];
[self.pageControl setNumberOfPages:2];
[self.navigationController.navigationBar addSubview:self.pageControl];
UITableViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = #[startingViewController];
[self.pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
CGFloat tabBarHeight = self.tabBarController.tabBar.frame.size.height;
// Change the size of page view controller
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - tabBarHeight);
[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];
}
Why don't you implement the UIPageViewControllerDelegate's
- pageViewController: didFinishAnimating: previousViewControllers: transitionCompleted: method? It has a transitionCompleted boolean which tells you if you should update your page control. Your current implementation seems to be buggy because as soon as you star swiping a page, it loads a view controller for the next page and calls the ...didShow... method. Hope this hepls.
I am trying to achieve a modal presentation effect where the presented view covers the parent view only partially as shown in the picture below.
I know I could achieve this by implementing custom transitions using UIPresentationController. I don't want to reinvent the wheel so before I roll on with development I would like to ask.
Is there a build in support for this kind of transition in the APIs?
I researched all available Modal Presentation Styles and it appears to me there is no support for the transition I want to make and the only way of achieving it is just to code it.
I ran into this exact same issue. I went down the modal presentation styles route as well and kept hitting a wall (specifically getting it working on an iPhone rather than an iPad).
After some digging around, I was able to get it working though. Here's how I did it:
To start, we need a view controller that we will be presenting (the modal one) to set it's view's background color to transparent and set the frame of the navigation controller's view to some offset.
ModalViewController.h
#import UIKit;
#class ModalViewController;
#protocol ModalViewControllerDelegate <NSObject>
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController;
#end
#interface ModalViewController : UIViewController
#property (weak, nonatomic) id<ModalViewControllerDelegate> delegate;
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;
#end
ModalViewController.m
static const CGFloat kTopOffset = 50.0f;
#implementation ModalViewController {
UINavigationController *_navController;
}
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
rootViewController.navigationItem.leftBarButtonItem = [self cancelButton];
_navController = [[UINavigationController alloc] initWithRootViewController:rootViewController];
self.view.backgroundColor = [UIColor clearColor];
[self.view addSubview:_navController.view];
// this is important (prevents black overlay)
self.modalPresentationStyle = UIModalPresentationOverFullScreen;
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGRect bounds = self.view.bounds;
_navController.view.frame = CGRectMake(0, kTopOffset, CGRectGetWidth(bounds), CGRectGetHeight(bounds) - kTopOffset);
}
- (UIBarButtonItem *)cancelButton
{
return [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStylePlain target:self action:#selector(cancelButtonClicked:)];
}
- (void)cancelButtonClicked:(id)sender
{
[_delegate modalViewControllerDidCancel:self];
}
#end
Next, we need to set up the presenting controller to run the following animation:
Scale itself down
Fade out a lil' bit
Present the modal view controller using presentViewController:animated:completion
This is what I did
PresentingViewController.m
static const CGFloat kTransitionScale = 0.9f;
static const CGFloat kTransitionAlpha = 0.6f;
static const NSTimeInterval kTransitionDuration = 0.5;
#interface PresentingViewController <ModalViewControllerDelegate>
#end
#implementation PresentingViewController
...
...
- (void)showModalViewController
{
self.navigationController.view.layer.shouldRasterize = YES;
self.navigationController.view.layer.rasterizationScale = [UIScreen mainScreen].scale;
UIViewController *controller = // init some view controller
ModalViewController *container = [[ModalViewController alloc] initWithRootViewController:controller];
container.delegate = self;
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.transform = CGAffineTransformMakeScale(kTransitionScale, kTransitionScale);
weakSelf.navigationController.view.alpha = kTransitionAlpha;
[weakSelf presentViewController:container animated:YES completion:nil];
} completion:^(BOOL finished) {
weakSelf.navigationController.view.layer.shouldRasterize = NO;
}];
}
#pragma mark - ModalViewControllerDelegate
- (void)modalViewControllerDidCancel:(ModalViewController *)modalViewController
{
__weak UIViewController *weakSelf = self;
[UIView animateWithDuration:kTransitionDuration animations:^{
weakSelf.navigationController.view.alpha = 1;
weakSelf.navigationController.view.transform = CGAffineTransformIdentity;
[weakSelf dismissViewControllerAnimated:YES completion:nil];
}];
}
#end
pretty sure its done like this
let newVC = <view controller you want to display>
let nav: UINavigationController = UINavigationController(rootViewController: newVC)
if let currVc = UIApplication.sharedApplication().keyWindow?.rootViewController {
nav.transitioningDelegate = currVc
nav.modalPresentationStyle = UIModalPresentationStyle.Custom;
currVc.presentViewController(nav, animated: true, completion: nil)
}
I'm pretty sure this is your answer - Page sheet - as in UIModalPresentationPageSheet
https://developer.apple.com/library/ios/documentation/userexperience/conceptual/mobilehig/Alerts.html#//apple_ref/doc/uid/TP40006556-CH14-SW3
I have a button, which when is pressed, adds a new subview with other buttons on it. I have done like this.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget: #selector(newView)];
- (void)newView
{
UIView *newView = [[UIView alloc]initWithFrame:CGRectmake(0,0,100,100)];
[self.view addSubview:newView];
}
Now when the button is pressed the new view is just added. I want to animate the new view like I can do in the IB, with push segue modal style. How can I do that programmatically?
You can do that with performSegueWithIdentifier, here is the code
- (void)newView
{
//execute segue programmatically
[self performSegueWithIdentifier: #"MySegue" sender: self];
}
Try something like this.
- (void)newView
{
[self.view addSubview:newView]
newView.frame = // somewhere offscreen, in the direction you want it to appear from
[UIView animateWithDuration:10.0
animations:^{
newView.frame = // its final location
}];
}
You can present a new viewcontroller like this:
- (void)newView
{
//Embed your new View into a plain UIViewController
UIView *newView = [[UIView alloc]initWithFrame:CGRectmake(0,0,100,100)];
UIViewController *yourNewViewController= [[UIViewController alloc] init];
controller.view = newView;
//Present it animated
[self presentViewController:yourNewViewController animated:YES completion:nil];
}
Hope it helps! :)
I'm trying to have something similar to a UINavigationController so I can customize the animations. To start, I'm just using Apple stock animations. Here's my containerViewController:
- (void)loadView {
// Set up content view
CGRect frame = [[UIScreen mainScreen] bounds];
_containerView = [[UIView alloc] initWithFrame:frame];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.view = _containerView;
}
- (id)initWithInitialViewController:(UIViewController *)vc {
self = [super init];
if (self) {
_currentViewController = vc;
[self addChildViewController:_currentViewController];
[self.view addSubview:_currentViewController.view];
[self didMoveToParentViewController:self];
_subViewControllers = [[NSMutableArray alloc] initWithObjects:_currentViewController, nil];
}
return self;
}
- (void)pushChildViewController:(UIViewController *)vc animation:(UIViewAnimationOptions)animation {
vc.view.frame = _containerView.frame;
[self addChildViewController:vc];
[self transitionFromViewController:_currentViewController toViewController:vc duration:0.3 options:animation animations:^{
}completion:^(BOOL finished) {
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
[self.subViewControllers addObject:vc];
}];
}
- (void)popChildViewController:(UIViewController *)vc WithAnimation:(UIViewAnimationOptions)animation {
// Check that there is a view controller to pop to
if ([self.subViewControllers count] <= 0) {
return;
}
NSInteger idx = [self.subViewControllers count] - 1;
UIViewController *toViewController = [_subViewControllers objectAtIndex:idx];
[vc willMoveToParentViewController:nil];
[self transitionFromViewController:vc toViewController:toViewController duration:0.3 options:animation animations:^{
}completion:^(BOOL finished) {
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
[self didMoveToParentViewController:toViewController];
[self.subViewControllers removeObjectAtIndex:idx];
}];
}
I have this ContainerViewcontroller as my rootViewController of the window. I can add my initial viewController and push a view controller. When I try to pop though, I get
ContainerViewController[65240:c07] Unbalanced calls to begin/end appearance transitions for <SecondViewController: 0x8072130>.
I'm wondering what I am doing wrong. I figured my initialViewController is still underneath the secondViewController. Any thoughts? Thanks!
I don't know if this is what's causing your problem, but shouldn't this:
[self didMoveToParentViewController:toViewController];
be:
[toViewController didMoveToParentViewController:self];
Also, I'm not sure what you're doing with the subViewControllers array. It seems to be a duplication of the childViewControllers array that is already a property of a UIViewController.
One other thing I'm not sure is right. In your pop method your toViewController is the last controller in the _subViewControllers array. Don't you want it to be the second to last? Shouldn't the last be the one you're popping? You're popping vc, which is a controller you're passing in to the method, I don't understand that.
This is the way I've made a navigation like controller. In its containment behavior, it acts like a navigation controller, but without a navigation bar, and allows for different transition animations:
#implementation ViewController
-(id)initWithRootViewController:(UIViewController *) rootVC {
if (self = [super init]) {
[self addChildViewController:rootVC];
rootVC.view.frame = self.view.bounds;
[self.view addSubview:rootVC.view];
}
return self;
}
-(void)pushViewController:(UIViewController *) vc animation:(UIViewAnimationOptions)animation {
vc.view.frame = self.view.bounds;
[self addChildViewController:vc];
[self transitionFromViewController:self.childViewControllers[self.childViewControllers.count -2] toViewController:vc duration:1 options:animation animations:nil
completion:^(BOOL finished) {
[vc didMoveToParentViewController:self];
NSLog(#"%#",self.childViewControllers);
}];
}
-(void)popViewControllerAnimation:(UIViewAnimationOptions)animation {
[self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[self.childViewControllers.count -2] duration:1 options:animation animations:nil
completion:^(BOOL finished) {
[self.childViewControllers.lastObject removeFromParentViewController];
NSLog(#"%#",self.childViewControllers);
}];
}
-(void)popToRootControllerAnimation:(UIViewAnimationOptions)animation {
[self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[0] duration:1 options:animation animations:nil
completion:^(BOOL finished) {
for (int i = self.childViewControllers.count -1; i>0; i--) {
[self.childViewControllers[i] removeFromParentViewController];
}
NSLog(#"%#",self.childViewControllers);
}];
}
After Edit: I was able to duplicate the back button function with this controller by adding a navigation bar to all my controllers in IB (including in the one that is the custom container controller). I added a bar button to any controllers that will be pushed, and set their titles to nil (I got some glitches if I left the title as "item"). Deleting that title makes the button disappear (in IB) but you can still make connections to it in the scene list. I added an IBOutlet to it, and added this code to get the function I wanted:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if (self.isMovingToParentViewController) {
self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -2] navigationItem].title;
}else{
self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -3] title];
}
}
I've shown two different ways that worked to access a title -- in IB you can set a title for the controller which I used in the else clause, or you can use the navigationItem title as I did in the if part of the clause. The "-3" in the else clause is necessary because at the time viewWillAppear is called, the controller that is being popped is still in the childViewControllers array.
addChildViewController should be called first
For adding / removing, you can refer to this great category and have no worry when to call it:
UIViewController + Container
- (void)containerAddChildViewController:(UIViewController *)childViewController {
[self addChildViewController:childViewController];
[self.view addSubview:childViewController.view];
[childViewController didMoveToParentViewController:self];
}
- (void)containerRemoveChildViewController:(UIViewController *)childViewController {
[childViewController willMoveToParentViewController:nil];
[childViewController.view removeFromSuperview];
[childViewController removeFromParentViewController];
}
In addition to rdelmar's answer you should not be calling addView/removeFromSuperview transitionFromViewController does this for you, from the documentation:
This method adds the second view controller’s view to the view
hierarchy and then performs the animations defined in your animations
block. After the animation completes, it removes the first view
controller’s view from the view hierarchy.
I have a rootViewController, in it's viewDidLoad method, I initialized another two ViewController2* object and their views as subview of rootViewController.view, then I set first ViewController2* controller.view.hidden = YES.
Then, on v1 has a button handler, when touch it, it present a UINavigationController, after that touch 'dismiss' button call dismissViewControllerAnimated on v1.
The question is: when dismiss complete, the two of ViewController2* fire viewWillAppear. How to make it only fire the viewWillAppear on the visible one, but not on the hidden one?
the rootViewController's implementation:
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.v1 = [[ViewController2 alloc] init];
self.v1.title = #"v1";
[self.view addSubview:self.v1.view];
self.v1.view.hidden = YES;
self.v2 = [[ViewController2 alloc] init];
self.v2.title = #"v2";
[self.view addSubview:self.v2.view];
UIButton * btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setTitle:#"POP" forState:UIControlStateNormal];
[btn sizeToFit];
[btn addTarget:self action:#selector(touchHandler:) forControlEvents:UIControlEventTouchDown];
[self.view addSubview:btn];
}
- (void)touchHandler:(id)sender {
UINavigationController * nc= [[UINavigationController alloc] initWithRootViewController:[[UIViewController alloc] initWithNibName:nil bundle:nil]];
((UIViewController *)[nc.viewControllers objectAtIndex:0]).navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"dismiss" style:UIBarButtonItemStyleBordered target:self action:#selector(dismissHandler:)];
[self presentViewController:nc animated:YES completion:nil];
}
- (void) dismissHandler:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
ViewController2:
#implementation ViewController2
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"%#",self.title);
}
#end
viewWillAppear will fire on your UIViewController's, even if the view controllers view is set hidden=YES.
You can surely test if (self.view.hidden == YES) in your viewWillAppear delegate method if you want to prevent some expensive operation from occurring, but beware that if you later make that view un-hidden, that viewWillAppear won't fire then.
Simple, the reason why those methods are called is because the viewController's view is part of the main window's view hierarchy. This means that it has a superview that has a superview that has a superview and so on until that superview is the main window.
Instead of hiding and unhiding the viewController views, you should instead add and remove them from their superview. Also, to make sure that viewWillAppear and viewDidAppear are called correctly at the correct times, take a look at ViewController Containment:
http://www.cocoanetics.com/2012/04/containing-viewcontrollers/