I need an sliding menu mentioned in the pic which is compatible for iOS 6 and iOS 7
You can check this tutorial. It will show you how to do this on both side of the phone.
If you don't want to read all of this :
The main idea is to have a view below the main view. When you slide or touch your menu button, you just move your top view by doing the following (i did this on a class inherited by all of my view controllers, but you're free to do it anywhere you want / need it) :
// self.isMenuOpened is a flag to save menu state and set the move distance (and a few other things)
// Init move distance
float move = 270; // 270 is arbitrary value, you can set it as you like
if (self.isMenuOpened) { move = -move; }
// Moves all view except menu
[UIView beginAnimations:#"openMenu" context:nil];
[UIView setAnimationDuration:kAnimationDurationShort];
// Navigation bar frame moving (if you have one)
CGRect navFrame = self.navigationController.navigationBar.frame;
self.navigationController.navigationBar.frame = CGRectMake(navFrame.origin.x + move,
navFrame.origin.y,
navFrame.size.width,
navFrame.size.height);
// Call switching move on each view
BOOL first = true;
for (UIView *view in [self.view subviews]) {
if (first) { first = false; } // First view is menu if it's on minimum zIndex
else {
CGRect mainViewFrame = view.frame;
[view setUserInteractionEnabled:self.isMenuOpened]; // Disable your main view as it still visible
view.frame = CGRectMake(mainViewFrame.origin.x + move, mainViewFrame.origin.y,
mainViewFrame.size.width, mainViewFrame.size.height);
}
}
[UIView commitAnimations];
I iterate through all of my subviews because, in a few cases, i added a view or two. But if you have a single view (and then your menu below), you don't have to do all the iteration stuff. Hope that helps !
You can use this awesome and simple library
https://github.com/arturdev/AMSlideMenu
which supports left and right menus and fully customizable
Related
I would like to emulate the swipe to left to show next picture seen in the photos app, among other places so that the new picture slides in from right to left using animation.
However, in my case, the view has more than one photo. It has a caption as well.
I am able to do this without animation by just updating the photo and text upon swipe. This just changes the photo and caption on screen, however, without the right to left animation technique.
Is there a way to show the old view moving to the left while the new updated view moves in from the right.
The following moves the view to the left but renders the screen black as nothing is put in its place.
//In handleSwipe called by gesture recognizer
CGRect topFrame = self.view.frame;
topFrame.origin.x = -320;
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.view.frame = topFrame; _myImage=newImage;
_mycaption=newcaption;} completion:^(BOOL finished){ }];
Thanks for any suggestions.
Edit:
The view in question is a detail view that you get to from a tableview through a segue. It currently already uses a scrollview for vertical scrolling as the content can exceed a single screen in the vertical dimension.
I have found a way to get the old photo and caption to move left and the new to come in from right using a completion block, however, it is less than satisfactory, because there is a gap between the two actions when the screen is black. This seems endemic to completion approach because new image does not start to move until old image has completed moving.
- (IBAction)swipedLeft:(id)sender
{
CGRect initialViewFrame = self.view.frame;
CGRect movedToLeftViewFrame = CGRectMake(initialViewFrame.origin.x - 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
CGRect movedToRightViewFrame = CGRectMake(initialViewFrame.origin.x + 375, initialViewFrame.origin.y, initialViewFrame.size.width, initialViewFrame.size.height);
[UIView animateWithDuration:0.1
animations:^{self.view.frame = movedToLeftViewFrame;
//above moves old image out of view.
}
completion:^(BOOL finished)
{
self.view.frame = movedToRightViewFrame;
[self loadNextItem];//changes pic and text.
[UIView animateWithDuration:0.1
animations:^{self.view.frame = initialViewFrame;
//moves new one in
}
completion:nil];
}];
}
I suggest using a UIPageViewController, and manage each image/set of images as a separate view controller.
A page view controller supports either a page curl style transition or a slide transition. You'd want the slide transition.
Three ways to build this using existing components:
UIPageViewController (see DuncanC's answer).
UICollectionView with pagingEnabled set to true. Use a full-screen-sized cell and a UICollectionViewFlowLayout with scrollDirection set to horizontal.
UICollectionView as above, but instead of setting pagingEnabled to true, implement scrollViewWillEndDragging:withVelocity:targetContentOffset: in your delegate. This allows you to have a gutter between each cell while still having each cell fill the screen. See this answer.
It's ScrollView+PageControl
I hope it can help you.
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.delegate =self;
self.scrollView.translatesAutoresizingMaskIntoConstraints =NO;
self.scrollView.pagingEnabled =YES;
self.scrollView.contentSize =CGSizeMake(ViewWidth*VIewCount, ViewHeight);
[self.view addSubview:self.scrollView];
self.pageControl = [[UIPageControl alloc] init];
self. pageControl.translatesAutoresizingMaskIntoConstraints =NO;
[self.view addSubview:self.pageControl];
self. pageControl.numberOfPages = VIewCount;
self.pageControl.currentPage = 0;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.0f]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[self.scrollView(width)]"
options:0
metrics:#{#"width":#(ViewWidth)}
views:NSDictionaryOfVariableBindings(self.scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-[self.scrollView(height)]"
options:0
metrics:#{#"height":#(HEIGHT_VIEW)}
views:NSDictionaryOfVariableBindings(self.scrollView)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[_scrollView]-5-[pageControl(15)]"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:NSDictionaryOfVariableBindings(self.scrollView, self.pageControl)]];
//[imgArr addObject:[UIImage imageNamed:...]];
for (NSInteger index = 0; index<ViewCount; index++) {
UIImageView *addSubview = [[UIImageView alloc] initWithFrame:CGRectMake(index*ViewWidth, 0, ViewWidth, ViewHeight)];
// addSubView.image = [imgArr objectAtIndex:index];
[self.scrollView addSubview:addSubview];
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
NSInteger currentOffset = scrollView.contentOffset.x;
NSInteger index = currentOffset / ViewWidth;
if (currentOffset % ViewWidth == 0) {
pageControl.currentPage = index;
}
}
I would prefer first download the project from below link and then continue with question (only 36kb)
Download Link
At start what I have is like below.
When I click My Office button, I am calling action actionSeenButton which will print NSLog(#"actionSeenButton");
- (IBAction)actionSeenButton:(id)sender {
NSLog(#"actionSeenButton");
}
This works perfect.
When I click, Show hidden button, I am sliding view by 100 and showing the image and buttons that I have at the top, as shown in below image
Code used is
- (IBAction)showHiddenButton:(id)sender {
CGAffineTransform translation = CGAffineTransformIdentity;
translation = CGAffineTransformMakeTranslation(0, 100);
[UIView beginAnimations:nil context:nil];
self.view.transform = translation;
[UIView commitAnimations];
}
When I click this button, I am calling action actionHiddenButton which will print NSLog(#"actionHiddenButton");
- (IBAction)actionHiddenButton:(id)sender {
NSLog(#"actionHiddenButton");
}
BUT the problem is, when I click the new button that I see, action is not getting called.
Any idea why this is happening?
Note
When I move the top hidden button from y=-70 to y=170, action is getting called.
Sample project can be downloaded from here
What I wanted to implement is, showing three buttons (as menu) on the top in one line by moving view down.
verify that your button is not behind the frame of another view. even if the button is visable, if there is something covering it up it wont work. i don't have access to xcode at the moment but my guess is your view "stack" is prohibiting you from interacting with the button. a button is esentually a uiview and you can do all the same animations to buttons and labels that you can with views. your best bet is to leave the view in the background alone and just move your buttons. since your "hidden" button isn't part of your main "view" hiarchy thats where your problem is.
upon further investigation, your problem is related to auto-layout and making sure your button object stays in the view hierarchy. if you turn off auto-layout you will see where the problem is. when you animate the main view down the "hidden" button is off of the view and there for inactive. the easiest solution is to just animate the buttons. the next best solution closest to what you have is to add another view onto your "main view" and then put the buttons into that view. also why do you have that background image twice? why not just set the background color of your view to that same yellow?
I downloaded your project and it seems the translation you're making for self.view. So the actionHiddenButton is not in the frame.Its better to have the controls you want to animate in the separate view.
If you want to see the problem, after your view get transformed set clipsToBounds to YES. Like
self.view.transform = translation;
self.view.clipsToBounds = YES;
Yipeee!!! Below is how I did.
.h
Added new variable.
#property (retain, nonatomic) NSString *hideStatus;
.m
-(void) viewDidAppear:(BOOL)animated {
NSLog(#"viewDidAppear");
CGAffineTransform translation = CGAffineTransformIdentity;
translation = CGAffineTransformMakeTranslation(0, -100);
self.view.transform = translation;
self.view.clipsToBounds = YES;
[UIView commitAnimations];
self.view.frame = CGRectMake(0,-80,320,560);
hideStatus = #"hidden";
}
- (IBAction)showHiddenButton:(id)sender {
NSLog(#"hideStatus===%#", hideStatus);
CGAffineTransform translation = CGAffineTransformIdentity;
if ([hideStatus isEqualToString:#"hidden"]) {
translation = CGAffineTransformMakeTranslation(0, 0);
hideStatus = #"shown";
} else {
translation = CGAffineTransformMakeTranslation(0, -100);
hideStatus = #"hidden";
}
[UIView beginAnimations:nil context:nil];
self.view.transform = translation;
self.view.clipsToBounds = YES;
[UIView commitAnimations];
}
Attached is the sample project. You can download from here.
I have a UITableView cell that has a custom label inside to handle the variable height. There is also an UIImage on the right and left.
When the table is toggled into edit mode, I want to inform and change each cell in the table, to format properly for being shifted to the right. And, when the user presses the small -, I want to further optimize to make room for the delete button.
Looking for a pattern that works for the above, assuming there is custom content in the cell that I have control over.
Thanks in advance!
Normaly all you need to do is set the springs and struts correctly and your content will slide correctly. If you create your sub-views in code then you need to make sure you call addSubview on the cell.contentView and not the cell.
To hide and / or resize the sub-views you need to override willTransitionToState:
- (void)willTransitionToState:(UITableViewCellStateMask)state
{
UIView *imageView = self.rightImageView;
UIView *labelView = self.centerTextLabel;
CGRect labelFrame = labelView.frame;
if (state & UITableViewCellStateShowingDeleteConfirmationMask) {
labelFrame.size.width += 52;
// Animating the fade while the image is sliding to the left
// is more jarring then just making it go away immediately
imageView.alpha = 0.0;
[UIView animateWithDuration:0.3 animations:^{
labelView.frame = labelFrame;
}];
} else if (!self.rightImageView.alpha) {
labelFrame.size.width -= 52;
[UIView animateWithDuration:0.3 animations:^{
imageView.alpha = 1.0;
labelView.frame = labelFrame;
}];
}
[super willTransitionToState:state];
}
I created a quick sample app up on GitHub that demos an iOS 4.3 app using a nib or code just uncomment //#define USE_NIB_TABLEVIEWCELL to use the nib
https://github.com/GayleDDS/TestTableCell.git
I personally prefer to create the table view cell in a nib file and only after profiling the App, replace the nib with code.
I am using content editable html loaded in uiwebview. I need the code to set the cursor position when the keyboard is hidden/shown.
Currently when i click on webview keyboard comes up but the content gets hidden behind the keyboard. Same happens when i keeps pressing on return key the cursor/text goes behind the webview or is not visible.
For head start, i need the functionality like used in iPad Evernote application. In that you can see cursor never goes behind the keyboard, it always starts above the keyboard.
I'm using javascript for this. I'm using a class to keep the code little more organized (so you'll see some this in the code), but that's not necessary.
// this is used to get the current coordinates of the selection - not very efficient, so it shouldn't be called too often
this.updateOffset = function() {
try{
var sel = window.getSelection();
range = sel.getRangeAt(0);
if(this.tmpSpan==null){
this.tmpSpan = document.createElement('span');
}
range.insertNode(this.tmpSpan);
this.yOffset = this.tmpSpan.offsetTop;
this.xOffset = this.tmpSpan.offsetLeft;
this.tmpSpan.parentNode.removeChild(this.tmpSpan);
}
catch(exc){
log('updateOffset:' + exc.toString());
}
}
// eContent is the div with 'contenteditable', while visibleHeight is an int, set from objective-c (depending on where the webview is positioned, keyboard height and screen height)
this.scrollToVisible = function(){
try {
if(this.eContent.clientHeight>this.visibleHeight){
this.updateOffset();
if(this.yOffset<window.pageYOffset){
window.scrollTo(0, this.yOffset);
}
else if(this.yOffset-window.pageYOffset>this.visibleHeight){
window.scrollTo(0, this.yOffset-this.visibleHeight);
}
}
}
catch (exc){
log('scrollToVisible: ', exc.toString());
}
}
In objective-c I'm setting the visibleHeight during keyboard showing up, and afterwards call scrollToVisible when keyboard has finished showing.
-(void)setVisibleHeight:(int)height{
[self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"docState.visibleHeight=%d", height]];
}
-(void)scrollToVisible{
[self stringByEvaluatingJavaScriptFromString:#"docState.scrollToVisible()"];
}
scrollToVisible is also called on the javascript events: onkeyup, onpaset, oncut, which fixes the issue when pressing 'return' or wrapping on multiple lines.
In case you decide to go this way, you'll need to be very careful when you scroll through javascript, otherwise it may cause some issues with UIWebview control (e.g: placing the cursor at the wrong positions, moving cursor automatically on the top of the document etc.)
Edit
Some clarification regarding the visibleHeight. From what I can remember, I used this because I wasn't able to get the actual visible height from javascript (document.body.clientHeight would also include the area behind the keyboard).
Since I'm presenting the UIWebView in full screen, I'm setting the visible height as follows:
- (void)keyboardWillShow:(NSNotification *)notification {
...
NSDictionary *userInfo = [notification userInfo];
NSValue* aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [aValue CGRectValue];
CGRect kbRect = [self.window convertRect:keyboardRect fromView:nil];
_kbRect = kbRect;
CGPoint sorigin = [self.superview convertPoint:self.frame.origin toView:nil];
int visibleHeight = _kbRect.origin.y-sorigin.y-_tlbInputAccessory.frame.size.height-lkPadBottom; // _tlbInputAccessory is a custom accessory view
[self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:#"docState.setVisibleHeight(%d)", height]];
...
}
Note that I'm calling this from a subclass of UIWebView, so self will represent the UIWebView control.
I have a series of UIButtons which I use to update a couple of UILabels and initiate animation of an object from offscreen to onscreen. The progression goes like this. Button 1 & 2 are visible and when the user pushes a button a UILabel updates and a new UIImageView animates onto the screen. This works fine the first iteration, on round two, buttons 3 and 4 which were hidden, become visible. Once the user selects either button 3 or 4, another UIImageView should animate onto the screen (on top of the other images... "stacking" them). What is happening is the UIImageView which animated on after the user pressed either button 1 or 2, disappears, and nothing animates. This only occurs if the UILabels have to update based on the button selection. In the UILabel doesnt need to update, the animation works fine.
Relevent Code:
-(IBAction)didTapSizeButton:(id) sender {
if ([sender tag] == 1) {
sizeLabel.text = #"12 oz";
}
else if ([sender tag] == 2) {
sizeLabel.text = #"24 oz";
}
if (coasterTwo.center.x != 150) {
[UIView animateWithDuration:0.35
delay:0
options:(UIViewAnimationOptionCurveEaseOut)
animations:^{
coasterTwo.center = CGPointMake(150, 220);
} completion:^(BOOL finished) {
}];
}
containerOne.hidden = NO;
containerTwo.hidden = NO;
}
-(IBAction)didTapContainerButton:(id)sender {
if ([sender tag] == 3) {
containerLabel.text = #"Bottle";
}
else if ([sender tag] == 4) {
containerLabel.text = #"Can";
}
if (coasterTwo.center.x == 150 && coasterThree.center.x != 150) {
[UIView animateWithDuration:0.35
delay:0
options:(UIViewAnimationOptionCurveEaseOut)
animations:^{
CGPoint center = coasterThree.center;
center.x += 305;
coasterThree.center = center;
}
completion:^(BOOL finished){
}];
}
}
The UIImageViews are obviously reset to their starting positions as soon as I reach back to update the UILabel... Im just not sure how to prevent that.
I would recommend using the static UIView commitAnimations method because you don't need the completion block:
[UIView beginAnimations:nil context:nil];
//Code for changing frames / alpha / anything else that is animatable
[UIView commitAnimations];
Try this out, see if it helps.
There's got to be something simple. As I said, the code for didTapContainerButton seems suspect (everytime you hit it coasterThree will be shifted another 305 points to the right), but there's nothing shown above that would cause coasterTwo to disappear. The only thing that would do that is if (a) some IB link is messed up (though it's not clear how that could result in images from disappearing when you hit button 3 or 4); (b) there's code you're not showing which might hide it; or (c) you've got some other control on your window that is hiding it (I've definitely seen situations where updating a label caused it to be brought to the front, obscuring something behind it ... you can diagnose that by making the label have a transparent background, explicitly move the image to the foreground, or try setting alphas to 0.5 so you can see through stuff while you debug).
To diagnose the first possibility, you'd have to upload your project to DropBox or something and we could take a look at it (if you're confortable doing that ... if not, make a copy of your project, get rid of anything confidential/proprietary, and then upload that).
To diagnose the second possibility, you could just update your question with the full code of the view controller's .m file.
To diagnose this third possibility, you could either make sure that before your animation of an image that you move it up front (before you animate coasterThree, do [self.view bringSubviewToFront:coasterThree];). You could also make sure that the backgroundColor of all of your controls (and if you have any container UIViews that you've added in IB, them, too) are transparent [UIColor clearColor]. You could also, just for diagnostic purposes, temporarily reduce the alpha so you can see through them (and thus if anything is blocking something else (you can do this in IB, click on the main UIView background, press command+A to select all of the controls, and change alpha to 0.5).
Bottom line, I'd pursue option 3, and if you don't have luck, either share your project or share a comprehensive code sample. If you don't feel comfortable doing that, I might suggest that you throw away your old NIB and rebuild it from scratch (in case there's something weird in the one you've got) or start building a whole new test project where you incrementally re-implement your UI and see if you can figure out where it went wrong.
But there's nothing inconsistent with UIImageViews animating and setting UILabel text value. Down below I have code with two buttons, two images that animate on and off, and a label that's updated, and it all works fine. (I had a crisis of confidence: I was starting to fear that there was some weird interaction with UILabels and animation.) Sigh.
- (void)viewDidLoad
{
[super viewDidLoad];
CGPoint center;
self.label.text = #"";
// move image 1 (dog image) off screen to the left
center = self.image1.center;
center.x = -0.5 * self.view.frame.size.width;
self.image1.center = center;
self.image1.hidden = YES;
// move image 2 (cat image) off screen to the right
center = self.image2.center;
center.x = 1.5 * self.view.frame.size.width;
self.image2.center = center;
self.image2.hidden = YES;
// Do any additional setup after loading the view.
}
// if you hit button 1 (dog) button, change label, and animate the dog image on and cat image off
- (IBAction)button1:(id)sender
{
self.label.text = #"Dog";
self.image1.hidden = NO;
[UIView animateWithDuration:0.5
animations:^{
CGPoint center;
// move image 1 (dog image) onscreen
center = self.image1.center;
center.x = 0.5 * self.view.frame.size.width;
self.image1.center = center;
// move image 2 (cat image) off screen to the right (if it's not there already)
center = self.image2.center;
center.x = 1.5 * self.view.frame.size.width;
self.image2.center = center;
}];
}
// if you hit button 2 (cat) button, change label, and animate the dog image off and cat image on
- (IBAction)button2:(id)sender
{
self.label.text = #"Cat";
self.image2.hidden = NO;
[UIView animateWithDuration:0.5
animations:^{
CGPoint center;
// move image 1 (dog image) off screen to the left
center = self.image1.center;
center.x = -0.5 * self.view.frame.size.width;
self.image1.center = center;
// move image 2 (cat image) on screen
center = self.image2.center;
center.x = 0.5 * self.view.frame.size.width;
self.image2.center = center;
}];
}
Same thing just happened to one of my students. To prove you're not going crazy... try running it in iOS 5.x simulator ( and hopefully observe the desired behavior).
For iOS 6, you'll either have to code some state preservation for your UIView -- or -- if you haven't grown to love 'em and think you can't live without 'em -- get rid of the constraints in the xib file by unchecking "Uses Autolayout" in the file inspector.
Hope this helps!