For example in Tweetbot's iPhone app. When you open up the app and new tweets come in, it will just be appended to the top of the UIScrollView and the current tweet you see did not get refreshed. How can I achieve the same thing effect?. Say I have a UIScrollView with a UIView added to the UIScrollView starting at origin 10,10.
I then downloaded a few items and I want to put it at 10,10.. so I basically need to shift this old item at 10,10 down right? If I do so then during that shifting user's will see it shifted, which is not the effect I want. Where as in Tweetbot's app it seems that nothing is being shifted around, it's just that you grow the area above the 10,10 and append new stuff's there.
How do I do this?
Basically I wanted to implement the insertRowAtIndexPath in a UIScrollView.
Will restate the question this way: how to add content to the top of a UIScrollView without moving the content that's already there (relative to it's current offset).
If this is the right question, the right answer is to do the add and shift down just as you suggested, but then scroll by the same height as added content, giving the illusion that the old content didn't move.
- (void)insertRowAtTop {
// not showing insert atIndexPath because I don't know how you have your subviews indexed
// inserting at the top, we can just shift everything down. you can extend this idea to
// the middle but it requires that you can translate from rows to y-offsets to views
// shift everything down
for (UIView *view in self.scrollView.subviews) {
if ([view isKindOfClass:[MyScrollViewRow self]]) {
MyScrollViewRow *row = (MyScrollViewRow *)view; // all this jazz so we don't pick up the scroll thumbs
row.frame = CGRectOffset(row.frame, 0.0, kROW_HEIGHT); // this is a lot easier if row height is constant
}
}
MyScrollViewRow *newRow = [[MyScrollViewRow alloc] initWithFrame:CGRectMake(0.0,0.0,320.0,kROW_HEIGHT)];
// init newRow
[self.scrollView addSubview:newRow];
// now for your question. scroll everything so the user perceives that the existing rows remained still
CGPoint currentOffset = self.scrollView.contentOffset
self.scrollView.contentOffset = CGPointMake(currentOffset.x, currentOffset.y + kROW_HEIGHT);
}
If you set the contentOffset without animating there wont be a visible scrolling animation. So if your new view is 200 points tall you can set the origin of the new view at (10,10) and the old view at (10,210) and set the contentOffset of the scroll view to (10,210) you should achieve the effect you intend. You'll also need to increase the contentSize of your scroll view to be big enough for all of the content it contains.
Related
I have two scrollview, one scrollview inside another and I want to scroll my main scrollview content first after that only I want to scroll subview scroll
CGFloat scrollOffset = texscrl.contentOffset.y;
if (scrollOffset == 0)
{
//This condition will be true when scrollview will reach to bottom
self.ArtistScroll.scrollEnabled=YES;
texscrl.scrollEnabled=YES;
}else
{
self.ArtistScroll.scrollEnabled=NO;
texscrl.scrollEnabled=YES;
}
Here, am using content offset for this.. so can anyone help me?
You can achieve the affect you describe by using a third scrollview to actually handle the touch gestures and manually set the contentOffset of the other scrollviews.
Here is how to achieve this for vertically scrolling content, which is I think what you are describing. In the code, outerScrollView is the main scrollview, innerScrollView is the sub-scrollview that is contained by the outer scrollview, and trackingScrollView is the third scrollview that only handles the touches - it has no content.
Create the three scrollviews such that trackingScrollView exactly covers outerScrollView. (I assume you will do this in XIB or storyboard so there is no code.)
Once your scrollviews have content (and whenever their contentSize or bounds change) you should set the contentSize of trackingScrollView:
self.trackingScrollView.contentSize = CGSizeMake(self.trackingScrollView.bounds.size.width,
self.outerScrollView.contentSize.height + self.innerScrollView.contentSize.height - self.innerScrollView.bounds.height);
This makes the content height be that of outerScrollView plus the scrollable distance of innerScrollView, allowing trackingScrollView to scroll over the total travel distance of both scrollviews.
Delegate for trackingScrollView and implement scrollViewDidScroll:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView == self.trackingScrollView) {
CGFloat const offsetY = self.trackingScrollView.contentOffset.y;
// Calculate the maximum outer scroll offset
CGFloat const maxOuterOffsetY = self.outerScrollView.contentSize.height - self.outerScrollView.bounds.height;
// Calculate the maximum inner scroll offset
CGFloat const maxInnerOffsetY = self.innerScrollView.contentSize.height - self.innerScrollView.bounds.height;
if (offsetY < maxOuterOffsetY) {
// Scroll is within the outer scroll area or the top bounce zone
self.outerScrollView.contentOffset = CGPointMake(0, offsetY);
self.innerScrollView.contentOffset = CGPointZero;
} else if (offsetY < maxOuterOffsetY + maxInnerOffsetY) {
// Scroll is within the inner scroll area
self.outerScrollView.contentOffset = CGPointMake(0, maxOuterOffsetY);
self.innerScrollView.contentOffset = CGPointMake(0, offsetY - maxOuterOffsetY);
} else {
// Scroll is within the bottom bounce zone
self.outerScrollView.contentOffset = CGPointMake(0, offsetY - maxInnerOffsetY);
self.innerScrollView.contentOffset = CGPointMake(0, maxInnerOffsetY);
}
} else {
// Handle other scrollviews as required
}
}
Effectively we divide the tracking scroll area into two parts. The top part controls the scrolling within the outer scrollview and the bottom part controls the inner scrollview.
Assuming bounce is turned on (which generally it should be) we also need to handle scrolling outside the scroll area. We want the bounce to show on the outer scrollview so the top bounce is handled implicitly by the first conditional. However, the bottom bounce has to be handled explicitly by the third conditional, otherwise we would see the bounce on the inner scrollview.
To be clear, in this solution the two content scrollviews (outerScrollView and innerScrollView) do not receive any touches at all; all input is going to trackingScrollView. If you want input to subviews of the content scrollviews you will need a more advanced solution. I believe this can be done by putting trackingScrollView behind outerScrollView, removing outerScrollView's gesture recognizers and replacing them with those from trackingScrollView. I have seen this technique presented in an old WWDC session on UIScrollView but I have not tried it myself.
I want to create a slide-down animation, like in the image in the link. When I press on "Previous", I want it to slide down to the center of the screen. I am not really sure how to implement that. Any suggestion would be really handy.
http://imageshack.us/photo/my-images/718/slideto.png/
This looks like a UITableView. You can place a custom cell with the button Previous, which will load the new data above and then call the scrollToRowAtIndexPath:atScrollPosition:animated: function to perform the animation.
The whole thing is pretty likely a UIScrollView of some sort (UITableViews are scroll views as well). When the buttons is pressed, either use setContentOffset:animated: or scrollRectToVisible:animated: to do the scrolling. The "magic" is just in calculating the correct offset or rect. I'd suggest going with setContentOffset:animated:. It should work roughly like this:
CGPoint p;
p.x = 0;
// Get middle of the view to be centered.
p.y = CGRectGetMidY(myViewThatShouldBeCentered.frame);
// Need to offset it by half the scroll view frame, otherwise
// you'd just see the lower half of the view peeking out at the
// top of the scroll view.
p.y -= CGRectGetHeight(myScrollView.frame) / 2;
[myScrollView setContentOffset:p animated:YES];
EDIT - I've worked out what I was originally doing wrong. I was changing the size of the UIScrollView, instead of the pattern subview. I have fixed that, but amended my question with the new problem this has thrown up.
I am making a notes section in my app with a lined-paper effect. The lines are on a separate UIScrollView which responds to scrollViewDidScroll: so the lines and text always move together. My lines are set up like this in viewWillAppear:
CGRect noteLinesRect = CGRectMake(self.view.bounds.origin.x,
self.view.bounds.origin.y,
self.view.bounds.size.width,
noteTextView.contentSize.height+self.view.bounds.size.height);
UIScrollView *anoteXLinesView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.noteXLinesView = anoteXLinesView;
[anoteXLinesView release];
LinePatternView *linesPattern = [[LinePatternView alloc] initWithFrame:noteLinesRect];
self.linesPatternView = linesPattern; [linesPattern release];
[self.noteXLinesView addSubview:self.linesPatternView];
[linesPattern release];
CGPoint newOffset = CGPointMake(self.noteTextView.contentOffset.x, noteTextView.contentOffset.y - NOTE_LINES_OFFSET);
self.noteXLinesView.contentOffset = newOffset;
[self.view insertSubview:self.noteXLinesView atIndex:0];
This works fine when the user first looks at a stored note - all the text is nicely underlined. But when they write more text, eventually they get to the bottom of the lines I created in viewWillAppear and are writing on 'blank paper'. So I need my lined-paper pattern to dynamically get bigger and smaller so it is always a bit bigger than the contentSize of my textView. I am trying to do that like this:
-(void)textViewDidChange:(UITextView *)textView
{
self.linesPatternView.frame = CGRectMake( self.linesPatternView.frame.origin.x, //-self.noteTextView.contentOffset.y+NOTE_LINES_OFFSET,
self.linesPatternView.frame.origin.y,
self.linesPatternView.frame.size.width,
noteTextView.contentSize.height+self.view.bounds.size.height );
}
The problem is, although the lined-paper pattern does increase in size, it doesn't add new lines at the bottom. Instead, the pattern stretches out and gets bigger as the view gets bigger. What am I doing wrong?
One of the solutions is to make 3 views, each containing the lines of the size of your scrollview frame on screen. You position the three one underneath the other in the scrollview and monitor the scrollview through its delegate.
When scrolling down you check:
As soon as the topmost one goes offscreen for more than Y pixels you remove it from the scrollview and insert it underneath the bottom one.
When scrolling up you check:
As soon as the bottommost one goes offscreen for more than Y pixels you remove it from the scrollview and insert it above the top one.
Is there a reason you’re not simply using a tiled pattern as your background view’s backgroundColor? UIColor’s +colorWithPatternImage: will let you set a background that’ll tile indefinitely and won’t stretch as your view resizes. If part of the background has to be different—like the top of your paper, for instance—you can just place an image view containing that at the top of your background view.
Fixed! In addition to the changes outlined in my edit to the question above, I just needed to call [self.linesPatternView setNeedsDisplay]; after the resize.
Aplogies, but this is another newbie iPhone/Objective-C question.
I'm working on my first iPhone app which is a simple RPN calculator. I have a table displaying the contents of the stack, and now I'd like to fine tune the display a little bit.
I would like for the last entry in the stack to display at the bottom of the table, with the text right aligned. The right alignment was easy with
cell.textLabel.textAlignment = UITextAlignmentRight;
and I can make the table peg the last entry to the bottom of the view with this code:
NSIndexPath *scrollIndexPath = [NSIndexPath indexPathForRow:(stackSize - 1) inSection:0];
[tableView scrollToRowAtIndexPath:scrollIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
which works great but only when there are more entries on the stack than will fit in the view. When the stack has few entries, they're still aligned to the top of the view.
I've played a bit with altering the height of the cell for the first stack entry such that the first cell fills the whole view and shrinks as new cells are added until there are enough stack entries to fill the view, at which point the cell height is left alone. This seemed promising, but I'm having some trouble getting the "big" cell to bottom align the label. (It's vertical center aligned, by default.)
As I was hacking away at the bottom alignment thing, I began to wonder if I'm making this more complicated than it needs to be. Any thoughts?
I don't know if this is kosher, but you can use view transforms to rotate the table 180° and then rotate each cell 180°. I use this trick to create sideways tables (90° instead of 180°) on an app I'm writing.
Another approach is to resize the table view as you add or remove rows. You update the height and move the table view accordingly. Something like this looks quite OK:
- (void)resizeTableView {
CGFloat tableViewHeight = [self tableView:self.tableView numberOfRowsInSection:0] * self.tableView.rowHeight;
// remember to check the height !!
CGRect frame = self.tableView.frame;
frame.size.height = tableViewHeight;
frame.origin.y = BOTTOM_OF_VIEW - tableViewHeight;
[UIView animateWithDuration:0.3 animations:^{
[self.tableView setFrame:frame];
}];
}
If you are using the default animations for insert and delete, remember to set them to UITableViewRowAnimationNone, otherwise it looks strange.
i have an scroll view and i am adding few views to that
when the user scrolls how to get the current visible view's tag.
then i can add some thing to that view...
its just like getting the indexpathrow in table view..
how to do that..?
Thanks
You basically want to check if the frame of the subviews inside the UIScrollView intersect the scrollview's frame (if you only want to determine partial visibility), or if the frame is contained in the other frame (if you want to determine full visibility).
However, in order to check if the subview's frame intersects and/or is contained in the scrollview's frame you need to translate it from the local coords inside the scrollview to the global coordinates outside the scrollview.
That is probably pretty confusing, so here is some code. This will loop through all the subviews of a scrollview and print out whether it is fully visibile or partially visible:
for (UIView *subview in scrollView)
{
CGRect globalRect = CGRectOffset(subview.frame, -scrollView.contentOffset.x, -scrollView.contentOffset.y);
CGRect scrollViewBounds = CGRectMake(0.0f, 0.0f, scrollView.bounds.size.width, scrollView.bounds.size.height);
if (CGRectContainsRect(scrollViewBounds, globalRect)) {
NSLog(#"FULLY VISIBLE");
} else if (CGRectIntersectsRect(scrollViewBounds, globalRect)) {
NSLog(#"PARTIALLY VISIBLE");
}
}
You could put this in a UIScrollViewDelegate method to do these checks while the user is scrolling the content around.