I need help developing a logic that navigates to the next page of a gridView. My current approach takes me to page 2 but does not advances any further than that.
Here is what I have tried so far.
NSInteger nextPage;
NSINteger currentPage;
UIView *pageView = [self.pageViews objectAtIndex:page];
if ((NSNull*)pageView == [NSNull null]) {
CGFloat width = frame.size.width;
currentPage= floor((gridView.contentOffset.x+width/2)/width);
nextPage = currentPage+1; // update the next page button;
}
Next page button
- (IBAction)nextPage:(id)sender {
CGRect frame = gridView.frame;
frame.origin.x = frame.size.width * nextPage;
frame.origin.y = 0;
[gridView scrollRectToVisible:frame animated:YES];
}
Thanks.
The solution turned out to be very simple. I added a global variable to track the number of times the nextPage button was clicked and replaced the
NSInteger nextPage; with
int clickedNumber =0;
- (IBAction)nextPage:(id)sender {
clickedNumber = clickedNumber+1;
CGRect frame = gridView.frame;
frame.origin.x = frame.size.width * clickedNumber;
frame.origin.y = 0;
[gridView scrollRectToVisible:frame animated:YES];
NSLog(#"%d",clickedNumber);
}
This way every time the button is clicked the number is increased and it goes to goes to the next page according the number of times the button is clicked.
It and works like a charm. :)
Related
I have a table view that I would like to scroll when a selected a cell and "pop" it out and move it around the table view. The table view scrolls just fine and the long press to select a cell works, but I'm not able to scroll the table view when I have a cell selected.
I read up on UIGestureRecognizerDelegate but I'm not sure that is making a difference. I also call these method
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Here is part of my code:
- (IBAction)longPressGestureRecognized:(id)sender {
UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
UIGestureRecognizerState state = longPress.state;
//longPress.delegate = self;
CGPoint location = [longPress locationInView:self.personTableView];
NSIndexPath *indexPath = [self.personTableView indexPathForRowAtPoint:location];
static UIView *snapshot = nil; ///< A snapshot of the row user is moving.
static NSIndexPath *sourceIndexPath = nil; ///< Initial index path, where gesture begins.
switch (state) {
case UIGestureRecognizerStateBegan: {
if (indexPath) {
sourceIndexPath = indexPath;
UITableViewCell *cell = [self.personTableView cellForRowAtIndexPath:indexPath];
// capture the color of the cell before blacking out
savedTextColor = cell.detailTextLabel.textColor;
// Take a snapshot of the selected row using helper method.
snapshot = [self customSnapshotFromView:cell];
// Add the snapshot as subview, centered at cell's center...
__block CGPoint center = cell.center;
snapshot.center = center;
snapshot.alpha = 0.0;
[self.personTableView addSubview:snapshot];
[UIView animateWithDuration:0.25 animations:^{
// Offset for gesture location.
center.y = location.y;
snapshot.center = center;
snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshot.alpha = 0.98;
// Black out.
cell.detailTextLabel.alpha = 0;
cell.textLabel.alpha = 0;
} completion:nil];
}
break;
}
case UIGestureRecognizerStateChanged: {
CGPoint center = snapshot.center;
center.y = location.y;
snapshot.center = center;
// Is destination valid and is it different from source?
if (indexPath && ![indexPath isEqual:sourceIndexPath]) {
// ... update data source.
[dataSingelton saveUpdatedPersonList:(int)indexPath.row sourceIndex:(int)sourceIndexPath.row];
//[[dataSingelton mutableDataArray] exchangeObjectAtIndex:indexPath.row withObjectAtIndex:sourceIndexPath.row];
// ... move the rows.
[self.personTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath];
// ... and update source so it is in sync with UI changes.
sourceIndexPath = indexPath;
}
break;
}
default: {
// Clean up.
UITableViewCell *cell = [self.personTableView cellForRowAtIndexPath:sourceIndexPath];
[UIView animateWithDuration:0.25 animations:^{
snapshot.center = cell.center;
snapshot.transform = CGAffineTransformIdentity;
snapshot.alpha = 0.1;
cell.detailTextLabel.alpha = 1;
cell.textLabel.alpha = 1;
} completion:^(BOOL finished) {
[snapshot removeFromSuperview];
snapshot = nil;
cell.hidden = NO;
}];
sourceIndexPath = nil;
break;
}
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (UIView *)customSnapshotFromView:(UIView *)inputView {
UIView *snapshot = [inputView snapshotViewAfterScreenUpdates:YES];
snapshot.alpha = 0.1;
snapshot.layer.masksToBounds = NO;
snapshot.layer.cornerRadius = 0.0;
snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0);
snapshot.layer.shadowRadius = 5.0;
snapshot.layer.shadowOpacity = 0.4;
return snapshot;
}
When I set the delegate to self like this:
longPress.delegate = self;
The able view scrolls, but then my selected cell doesn't move. What am I missing to make this work?
* Updated *
After doing some more reading/research I think in order for me to achieve the effect I want (the scrolling effect on the TableView in Apple's weather app) I think that my long press gesture will also have to handle the scrolling of my table view. The default scrolling starts to scroll the entire table instantly, where as I need the table to scroll only when the long pressed cell is at the top or the bottom of my TableView. Am I correct in thinking like this? I've been trying to find an example to break down and study, but haven't had any luck finding anything like that.
** Updated **
So I'm trying to figure out how to trigger the scroll up timer when the user drags a cell up towards the top. The problem I'm having right now is figuring out when that cell is close to the top, when its at the very top of the tableview, thats easy I could use something like tableViewHight + 100, but this code wont work when the tableview is scrolled down. Here is what I have so far:
NSLog(#"This is table view hight %.2f", tableViewHight);
NSLog(#"This is content offset %f", (personTableView.contentOffset.y));
NSLog(#"Should be 100 place holder %.2f", (tableViewHight - tableViewHight) + personTableView.contentOffset.y + 100);
NSLog(#"location y %f", location.y);
// gets called repeatedly
// Step 1: check to see if we need to start scrolling
if (location.y < tableViewHight - tableViewHight + 100) {
// ** top **
// Step 2: make sure the current y postoin isn't greater then the previous y postion
if (location. y <= originalLocation.y) {
// NSLog(#"This is the offset y %f", self.personTableView.contentOffset.y);
// Step 4: check to see if we have a timer and if not create one
[self startTimer];
}
else{
//Step 3: they started to scroll up so end the timer
[self endTimer];
}
}
else if(location.y > tableViewHight - 100)
{
// ** bottom **
// Step 2: make sure the current y postoin isn't less then the previous y postion
if (location. y >= originalLocation.y) {
NSLog(#"its less then 0");
if (!timer) {
// Step 4: check to see if we have a timer and if not create one
[self startTimer];
}
}
else{
//Step 3: they started to scroll up so end the timer
[self endTimer];
}
}
else{
// ** middle **
// Step 2: check to see if we have a timer and if so destory it
[self endTimer];
}
Basically I want to trigger that if statement when the cell is less then 100px from the hight of the scrollable table view.
From the UILongPressGestureRecognizer you can get the current y position of the touch. From that you can calculate how close to the bottom or top of the UITableViewthe touch is. If you determine that it's close enough to begin scrolling, then start a repeating NSTimer that increments or decrements the contentOffset of your UITableView by 1 pixel, scrolling your table. Change how fast it scrolls by changing the timeInterval of the timer.
When the UILongPressGestureRecognizer ends, or if the y position of the touch moves outside of the scrolling zone, then invalidate your timer, which will stop the scrolling.
If you felt adventurous you could also increase the speed of the scrolling dynamically as the user gets closer to the edge of the UITableView by changing the timeInterval as the user moves their touch up or down, as that is generally how this type of functionality works.
Good luck!
In part of my app, a button click creates a new page. However, I want that page to be exactly the same as the one I was on previously, but just a new instance of it. After asking the best way to do this on SO, I learned the UIScrollView with paging enabled was the best approach. So, I wrote some code to try to do what I was trying. However, when I press the button, the app just crashes. I'm assuming it got into some kind of recursion (calling itself again and again). Any insight into what might be wrong would be great. I can confirm that all the outlets are hooked up to their respective positions in StoryBoard.
-(IBAction)createContainer:(id)sender {
[allScrollViews addObject:self.containerView];
for (int i = 0; i < allScrollViews.count; i++) {
CGRect frame;
frame.origin.x = self.containerView.frame.size.width * i;
frame.origin.y = 0;
frame.size = self.containerView.frame.size;
UIView *subview = self.view;
subview.backgroundColor = self.view.backgroundColor;
[self.containerView addSubview:subview];
}
self.containerView.contentSize = CGSizeMake(self.containerView.frame.size.width * allScrollViews.count, self.containerView.frame.size.height);
}
Once again, pressing the button has to pretty much duplicate the view that I'm on (ViewController.m), and just make a new instance of it on a new page. My code is hopefully close to what I am trying to accomplish.
Thanks much!
~Carpetfizz
EDIT: If it helps, this gets highlighted when the app crashes:
0x2281b61: calll 0x2281b66 CA::Layer::ensure_transaction_recursively(CA::Transaction*) + 14
I think you should make like this:
-(IBAction)createContainer:(id)sender {
[allScrollViews addObject:self.containerView];
for (int i = 0; i < allScrollViews.count; i++) {
CGRect frame = self.containerView.frame;
frame.origin.x = self.containerView.frame.size.width * i;
frame.origin.y = 0;
self.containerView.frame = frame;
[self.scrollView addSubview: self.containerView];
}
self.scrollView.contentSize = CGSizeMake(self.containerView.frame.size.width * allScrollViews.count, self.containerView.frame.size.height);
}
Alright so i was having a little bit of a problem with the view when the device went into landscape so i decided to change the location of my textfields by code with this:
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
UIInterfaceOrientation orientation = [[UIApplication sharedApplication]statusBarOrientation];
NSLog(#"SPIN");
if (orientation == UIDeviceOrientationPortrait || orientation == UIDeviceOrientationPortraitUpsideDown) {
CGRect frame = [self.a frame];
frame.origin.x = 20;
frame.origin.y = 57;
[self.a setFrame:frame];
CGRect frame2 = [self.b frame];
frame2.origin.x = 20;
frame2.origin.y = 132;
[self.b setFrame:frame2];
CGRect frame3 = [self.c frame];
frame3.origin.x = 20;
frame3.origin.y = 207;
[self.c setFrame:frame3];
} else {
CGRect frame = [self.a frame];
frame.origin.x = 83;
frame.origin.y = 21;
[self.a setFrame:frame];
CGRect frame2 = [self.b frame];
frame2.origin.x = 204;
frame2.origin.y = 21;
[self.b setFrame:frame2];
CGRect frame3 = [self.c frame];
frame3.origin.x = 330;
frame3.origin.y = 21;
[self.c setFrame:frame3];
}
}
it worked just fine, everything went where it was suppost to, the problem is when click on any of the textfields to write something all of them bug out and end up looking like this:
uh.. nevermind that apparently i need at least 10 reputation before i can post an image, so here's a description of what the image showed instead:
image shows 3 uisegmented controls and 3 textfields crammed stacked on top of each other on the very top of the screen
So i'm more of a hobbyist than a pro at this so i apologize if this is a stupid question, but what am i doing wrong here? and additionaly is there a better way of going about adapting your view?
Try using auto layout or may be auto resizing masks on different views which you are using. There is a property called as autoresizingmask in uiview.
You can check this for reference:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIView_Class/UIView/UIView.html
I am using a UIPanGestureRecognizer to allow my UITableView to be dragged around. I have currently set it so that the UITableView cannot be dragged past 0 or half its width. But now, its frame is set to 0 when I try and drag the UITableView back to 0 from an origin of greater than 0. How can I prevent this and allow dragging of the UITableView back to 0? I have tried the following, but I can't quite find out why the outlined code is causing this.
- (void) handlePan:(UIPanGestureRecognizer *) pan {
CGPoint point = [pan translationInView:_tableView];
CGRect frame = [_tableView frame];
if (point.x <= _tableView.frame.size.width / 2) {
frame.origin.x = point.x;
}
NSLog(#"%f : %f", frame.origin.x, _tableView.frame.origin.x);
//outline begin!
if (frame.origin.x < 0 && _tableView.frame.origin.x >= 0) {
frame.origin.x = 0;
}
//outline end!
isFilterViewShowing = frame.origin.x > 0;
[_tableView setFrame:frame];
}
This is not the prettiest code, but that is working in the simulator.
For this code to work you need to add an instance variable.
This code may not behave exactly as you want it, because it's keeping track of "negative" x position, so you get some "threshold" effect that you may not want depending on you design choice.
- (void) handlePan:(UIPanGestureRecognizer *) pan {
if (pan.state == UIGestureRecognizerStateBegan)
{
// cache the starting point of your tableView in an instance variable
xStarter = _tableView.frame.origin.x;
}
// What is the translation
CGPoint translation = [pan translationInView:self.tableView];
// Where does it get us
CGFloat newX = xStarter + translation.x;
CGFloat xLimit = self.tableView.superview.bounds.size.width / 2;
if (newX >= 0.0f && newX <= xLimit)
{
// newX is good, don't touch it
}
else if (newX < 0)
{
newX = 0;
}
else if (newX > xLimit)
{
newX = xLimit;
}
CGRect frame = self.tableView.frame;
frame.origin.x = newX;
[_tableView setFrame:frame];
if (pan.state == UIGestureRecognizerStateEnded)
{
// reset your starter cache
xStarter = 0;
}
}
Have you notice how [pan translationInView:aView]; is returning the offset of the pan gesture and not the position of the finger on the screen.
That is why your code don't work as you expect.
I have an app based on Apple's PageControl sample. The first time the view loads, the scroll view is loaded with page 0 and page 1. Whenever a scroll is initiated, the scrollViewDidScroll method should get called by UIKit correct?
When initiating a scroll from page 0 to page 1, the app should load page-1, page and page+1, (to prevent flashes during scrolling).
My app seems to call scrollViewDidScroll 19 times and my loadScrollViewWithPage: method 19 times each with page 0 and page 1, before it finally gets to page 1 and 2, then it crashes.
These are the methods:
- (void)scrollViewDidScroll:(UIScrollView *)sender {
NSLog(#"scrollviewdidscroll");
// We don't want a "feedback loop" between the UIPageControl and the scroll delegate in
// which a scroll event generated from the user hitting the page control triggers updates from
// the delegate method. We use a boolean to disable the delegate logic when the page control is used.
if (pageControlUsed) {
// do nothing - the scroll was initiated from the page control, not the user dragging
return;
}
// Switch the indicator when more than 50% of the previous/next page is visible
CGFloat pageWidth = scrollView.frame.size.width;
int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
pageControl.currentPage = page;
// load the visible page and the page on either side of it (to avoid flashes when the user starts scrolling)
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
// A possible optimization would be to unload the views+controllers which are no longer visible
}
- (void)loadScrollViewWithPage:(int)page {
if (page < 0) return;
if (page >= kNumberOfPages) return;
NSLog(#"page: %i", page);
// replace the placeholder if necessary
KeyboardViewController *controller = [viewControllers objectAtIndex:page];
if ((NSNull *)controller == [NSNull null]) {
controller = [[KeyboardViewController alloc] initWithPageNumber:page];
[viewControllers replaceObjectAtIndex:page withObject:controller];
[controller release];
}
// add the controller's view to the scroll view
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * page;
frame.origin.y = 0;
frame.size.height = scrollView.frame.size.height;
controller.view.frame = frame;
[scrollView setAutoresizesSubviews:YES];
[scrollView setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
[scrollView addSubview:controller.view];
}
Why would scrollViewDidScroll get called so many times?
Thanks
scrollViewDidScroll: gets called every time the scroll bounds change. This means it gets called during the scroll, as well as when it starts. You may want to try scrollViewWillBeginDragging: instead.