In my application i have webview and some items above the webview.when i scroll the webview i want to scroll these items also.i tried so many thing it is not possible in scrollViewDidScroll.i need something else.I tried these codes but it is not working efficiently..
- (void)scrollViewDidScroll:(UIScrollView *)sender {
CGRect webViewFrame = self.contentWebView.frame;
webViewFrame.origin.y -= 3;
CGRect imageViewFrame = self.featuredImageView.frame;
imageViewFrame.origin.y -= 3;
CGRect overlayFrame = self.overlayView.frame;
overlayFrame.origin.y -= 3;
[UIView animateWithDuration:0.001 animations:^{
self.contentWebView.frame = webViewFrame;
self.featuredImageView.frame = imageViewFrame;
self.overlayView.frame = overlayFrame;
}completion:^(BOOL finished) {
CGPoint offset2 = sender.contentOffset;
offset2.y = 0;
sender.contentOffset = offset2;
}];
}
Try to add subView (Other items) to your webView.scrollView ;)
Example: [myWebView.scrollView addSubView: myItem];
Related
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'm using the following code to hide a view and the space taken by the view based on a condition in viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
Data* data = [Data shared];
if (data.something == 0) {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 0;
self.tableView.tableHeaderView.frame = frame;
self.tableView.tableHeaderView.hidden = YES;
} else {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 44;
self.tableView.tableHeaderView.frame = frame;
self.tableView.tableHeaderView.hidden = NO;
}
}
The above code works, but I'm pretty sure that is not the right way to do that. I tried to set the tableHeaderView to nil, but once the code is called, the headerView is gone until the UITableView is destroyed (I think I can fix it using a IBOutlet to the tableHeader, but doesn't sounds right too.
UPDATE1: another try, but the code doesn't work:
- (CGFloat)tableView:(UITableView*)tableView heightForHeaderInSection:(NSInteger)section {
self.tableView.tableHeaderView.hidden = YES;
return 0;
}
The data source method tableView:heightForHeaderInSection: actually has nothing to do with the view that is associated with the table view's tableViewHeader property. There are two different types of headers here, the one header at the top of the tableView, in which can be placed things like a search bar, and the multiple headers that can be made to occur one per section within the table view.
To my knowledge, the tableViewHeader view is typically configured in the nib file, and I don't know that the table view calls any data source methods that allow any configuration for it, so you would have to do it manually. Frankly, if your code works, that would be a good way to do it. Hiding it would make the table view still act as if it's there...removing it entirely makes it so you can't get it back because it gets deallocated.
(However, as you said, you could use an IBOutlet pointing to the header view, as long as you make it a strong reference, and then you could somehow reinsert it into the table later. ...Hm, although the mechanics of how you add a view into the table view's scroll view, and position it correctly, is probably just annoying.)
My only suggestion would be animating the frame height to zero so you get a nice transition effect, something like animateWithDuration. But yeah, I would say you have the best method figured out already.
EDIT:
Code, you say? I take that as a challenge :)
- (void)setTableViewHeaderHidden:(BOOL)hide
{
// Don't want to muck things up if we are mid an animation.
if (self.isAnimatingHeader) {
return;
}
// This is our IBOutlet property, I am just saving a bit of typing.
UIView *theHeader = self.theHeaderView;
if (hide) {
// Save the original height into the tag, should only be done once.
if (!theHeader.tag) {
theHeader.tag = theHeader.frame.size.height;
}
// Transform and hide
if (theHeader.frame.size.height > 0) {
self.isAnimatingHeader = YES;
// New frame...
CGRect frame = theHeader.frame;
frame.size.height = 0;
// Figure out some offsets here so we prevent jumping...
CGPoint originalOffset = self.tableView.contentOffset;
CGPoint animOffset = originalOffset;
animOffset.y += MAX(0, theHeader.tag - animOffset.y);
CGPoint newOffset = originalOffset;
newOffset.y = MAX(0, newOffset.y - theHeader.tag);
// Perform the animation
[UIView animateWithDuration:0.35
delay:0.0
options: UIViewAnimationCurveEaseOut
animations:^{
theHeader.frame = frame;
self.tableView.contentOffset = animOffset;
}
completion:^(BOOL finished){
if (finished) {
// Hide the header
self.tableView.tableHeaderView = nil;
theHeader.hidden = YES;
// Shift the content offset so we don't get a jump
self.tableView.contentOffset = newOffset;
// Done animating.
self.isAnimatingHeader = NO;
}
}
];
}
} else {
// Show and transform
if (theHeader.frame.size.height < theHeader.tag) {
self.isAnimatingHeader = YES;
// Set the frame to the original before we transform, so that the tableview corrects the cell positions when we re-add it.
CGRect originalFrame = theHeader.frame;
originalFrame.size.height = theHeader.tag;
theHeader.frame = originalFrame;
// Show before we transform so that you can see it happen
self.tableView.tableHeaderView = theHeader;
theHeader.hidden = NO;
// Figure out some offsets so we don't get the table jumping...
CGPoint originalOffset = self.tableView.contentOffset;
CGPoint startOffset = originalOffset;
startOffset.y += theHeader.tag;
self.tableView.contentOffset = startOffset; // Correct for the view insertion right off the bat
// Now, I don't know if you want the top header to animate in or not. If you think about it, you only *need* to animate the header *out* because the user might be looking at it. I figure only animate it in if the user is already scrolled to the top, but hey, this is open to customization and personal preference.
if (self.animateInTopHeader && originalOffset.y == 0) {
CGPoint animOffset = originalOffset;
// Perform the animation
[UIView animateWithDuration:0.35
delay:0.0
options: UIViewAnimationCurveEaseIn
animations:^{
self.tableView.contentOffset = animOffset;
}
completion:^(BOOL finished){
// Done animating.
self.isAnimatingHeader = NO;
}
];
} else {
self.isAnimatingHeader = NO;
}
}
}
}
Built this in the table view template that comes with Xcode. Just to throw it together I used a UILongPressGestureRecognizer with the selector outlet pointing to this method:
- (IBAction)longPress:(UIGestureRecognizer *)sender
{
if (sender.state != UIGestureRecognizerStateBegan) {
return;
}
if (self.hidingHeader) {
self.hidingHeader = NO;
[self setTableViewHeaderHidden:NO];
} else {
self.hidingHeader = YES;
[self setTableViewHeaderHidden:YES];
}
}
And, I added these to my header:
#property (strong, nonatomic) IBOutlet UIView *theHeaderView;
#property (nonatomic) BOOL hidingHeader;
#property (nonatomic) BOOL isAnimatingHeader;
#property (nonatomic) BOOL animateInTopHeader;
- (IBAction)longPress:(id)sender;
Anyway, it works great. What I did discover is that you definitely have to nil out the table view's reference to the header view or it doesn't go away, and the table view will shift the cells' position based on the height of the frame of the header when it is assigned back into its header property. Additionally, you do have to maintain a strong reference via your IBOutlet to the header or it gets thrown away when you nil out the table view's reference to it.
Cheers.
Instead of,
if (1 == 1) {
CGRect frame = self.viewHeader.frame;
frame.size.height = 0;
self.viewHeader.frame = frame;
self.viewHeader.hidden = YES;
}
use it as,
if (1 == 1) {
self.viewHeader.hidden = YES;
}
If you do not want the view anymore instead of just hiding, use [self.viewHeader removeFromSuperview];
And if you want to add it after removing [self.view addSubview:self.viewHeader]; All these depends on your requirement.
Update:
for eg:-
if (data.something == 0) {
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView.hidden = YES;
} else {
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView.hidden = NO;
}
or,
if (data.something == 0) {
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView = nil;
} else {
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView = self.headerView; //assuming that self.headerview is the tableHeaderView created while creating the tableview
}
Update2: Here is a very simple version of animation block.
if (data.something == 0) {
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseOut
animations:^{
//set frame1 as frame without tableHeaderView
self.tableView.frame = frame1;
self.tableView.tableHeaderView.hidden = YES; // or self.tableView.tableHeaderView = nil;
}
completion:^(BOOL finished){
//if required keep self.tableView.frame = frame1;
}
];
} else {
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseIn
animations:^{
//set frame2 as frame with tableHeaderView
self.tableView.frame = frame2;
self.tableView.tableHeaderView.hidden = NO;// or self.tableView.tableHeaderView = self.headerView;
}
completion:^(BOOL finished){
//if required keep self.tableView.frame = frame2;
}];
}
I subclassed a UIImageView. When it is double tapped, it will enlarge itself by 40%. I also have a UILabel that i place in the custom UIImageView and would like it to enlarge by 40%.
i can't seem to get it to animate correctly.
-(void)enlargeImage
{
CGPoint pointCenter = self.center;
CGPoint labelCenter = self.displayNameLabel.center;
[UIView animateWithDuration:1
delay:0
options:(UIViewAnimationOptionAllowUserInteraction)
animations:^{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width * (1 + self.sizeMultiplier),
self.frame.size.height * (1 + self.sizeMultiplier));
// Commented out after setting the autoresizeMask on the label
/*
self.displayNameLabel.frame = CGRectMake(self.displayNameLabel.frame.origin.x,
self.displayNameLabel.frame.origin.y,
self.displayNameLabel.frame.size.width * (1 + self.sizeMultiplier),
self.displayNameLabel.frame.size.height * (1 + self.sizeMultiplier));
*/
self.layer.shadowOpacity = 1;
}
completion:^(BOOL finished)
{
[self callDelegateSelect];
}];
self.center = pointCenter;
//self.displayNameLabel.center = labelCenter;
//self.displayNameLabel.center = CGPointMake((self.frame.size.width / 2), (self.frame.size.height / 2));
self.isSelected = TRUE;
}
-(void)shrinkImageWithZoomScale:(float)zoomScale
{
CGPoint pointCenter = self.center;
CGPoint labelCenter = self.displayNameLabel.center;
[UIView animateWithDuration:1
delay:0
options:(UIViewAnimationOptionAllowUserInteraction)
animations:^{
self.frame = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
(self.originalHeight / zoomScale),
(self.originalHeight / zoomScale));
// Commented out after setting the autoresizeMask on the label
/*
self.displayNameLabel.frame = CGRectMake(self.displayNameLabel.frame.origin.x,
self.displayNameLabel.frame.origin.y,
((self.originalLableHeight) / zoomScale),
((self.originalLableHeight) / zoomScale));
*/
self.layer.shadowOpacity = 0;
}
completion:^(BOOL finished)
{
[self callDelegateDeselect];
}];
self.center = pointCenter;
//self.displayNameLabel.center = labelCenter;
//self.displayNameLabel.center = CGPointMake((self.frame.size.width / 2), (self.frame.size.height / 2));
self.isSelected = FALSE;
}
the custom UIImageView animates exactly how i want it too, but the UILabel misbehaves.
unselected (UILabel's background is set to orange):
selected:
i'm guessing since the UIImageView's point reference is changing, its messing up the points i'm telling the UILabel to be?
EDIT:
the custom UIImageView's:
self.autoresizesSubviews = TRUE;
the subview UILabel:
self.displayNameLabel.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
this solves the sizing issue, but it does not grow from the center; when growing, it shifts right and animates left to the center. when shrinking, it shifts left and animates right to the center. I also commented out setting the label's frame in the animation code.
If the UILabel is a subview of the UIImageView, you could try to set the autoresizing options of the UILabel so that it resizes automatically when its superview does.
Also, make sure the autoresizesSubviews property of the UIImageView is set to YES (which is the default by the way).
EDIT:
I uploaded my implementation to github. Maybe it is better to understand what I want and what my problem is.
I changed the code in the github project a little bit than the posted code here. I think the implementation in the github project is better, but not perfect.
What I want to do:
Have a UIScrollView with movable UIViews (e.g. Images). The user can pan this subviews and zoom in and out. When the user zooms in and moves a subview over the current visible area the scrollView should automatically scroll. As lang as the subview is over the edge the scrollview should scroll. When the subview isn't over the visible area anymore the scrollview should stop moving.
I try to explain my problem as good as possible.
What I have managed to do:
Zoom the scrollview, move the subviews with the UIPanGestureRecognizer, recognize when the subview is over the visible area and start moving (changing the contentOffset) the scrollview. Here I using a NSTimer to move the scrollview as long as the subview is over the visible area.
My problem:
When the subview is over the visible area a NSTimer is started, to change the contentOffset of the subview and the frame (position) of the subview.
After that I can't pan the subview anymore.
I can't figure out how to implement the pan gesture with changing the subview frame in a correct way.
My implementation:
I am using three views:
UIScrollView
MyImageContainerView (UIView, added as a subview to the scrollview)
MyImageView (UIView, added as a subview to MyImageContainerView)
Currently MyImageContainerView manages the workflow. A MyImageView has a UIPanGestureRecognizer attached. The method for this recognizer is implemented in MyImageContainerView:
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
//UIView which is moved by the user
MyImageView *currentView = gestureRecognizer.view;
switch (gestureRecognizer.state)
{
case UIGestureRecognizerStatePossible:
{
break;
}
case UIGestureRecognizerStateBegan:
{
//save both values in global instance variables
currentFrameOriginX = currentView.frame.origin.x;
currentFrameOriginY = currentView.frame.origin.y;
//global BOOL variable, to check if scrollView movement is performed
scrolling = NO;
break;
}
case UIGestureRecognizerStateChanged:
{
CGRect rect = CGRectMake(currentFrameOriginX + [gestureRecognizer translationInView:currentView.superview].x, currentFrameOriginY + [gestureRecognizer translationInView:currentView.superview].y, currentView.frame.size.width, currentView.frame.size.height);
if (CGRectContainsRect(currentView.superview.frame, rect)) {
/*PROBLEM: Here is a problem. I need this change of the frame here, to move the UIView along the movement from the user. In my autoScroll-method I have to set the frame of currentView, too. But I can't set the frame of currentView here and in the autoScroll. But as long as the NSTimer runs and is calling autoScroll: this if-statement isn't called, so I can't move the UIView with my finger anymore. */
if (!scrolling) {
//currently the NSTimer method for the automatically scrolling isn't performed, so:
//change the frame according to the pan gesture
currentView.frame = rect;
}
UIScrollView *scrollView = self.myScrollView; //reference to the "root" UIScrollView
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = scrollView.bounds.size;
CGRect frame = currentView.frame;
CGFloat scale = 1.0 / scrollView.zoomScale;
visibleRect.origin.x *= scale;
visibleRect.origin.y *= scale;
visibleRect.size.width *= scale;
visibleRect.size.height *= scale;
CGSize scrollZone = CGSizeMake(10.0f, 10.0f);
float scrollStep = 3.0f;
CGPoint scrollAmount = CGPointZero;
//determine the change of x and y
if (frame.origin.x+scrollZone.width < visibleRect.origin.x) {
scrollAmount.x = -scrollStep;
}
else if((frame.origin.x+frame.size.width)-scrollZone.width > visibleRect.origin.x + visibleRect.size.width) {
scrollAmount.x = scrollStep;
}
else if (frame.origin.y+scrollZone.height < visibleRect.origin.y) {
scrollAmount.y = -scrollStep;
}
else if((frame.origin.y+frame.size.height)-scrollZone.height > visibleRect.origin.y + visibleRect.size.height) {
scrollAmount.y = scrollStep;
}
if ((scrollAmount.x != 0) | (scrollAmount.y != 0)) {
if (![scrollTimer isValid]) {
//scrollTimer is a global NSTimer instance variable
[scrollTimer invalidate];
scrollTimer = nil;
NSString *scrollString = NSStringFromCGPoint(scrollAmount);
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:scrollString, #"scrollString", currentView, #"currentView", nil];
scrollTimer = [[NSTimer alloc]initWithFireDate:[NSDate date] interval:0.03f target:self selector:#selector(autoScroll:) userInfo:info repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:scrollTimer forMode:NSRunLoopCommonModes];
}
}
else {
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
}
}
break;
}
case UIGestureRecognizerStateEnded:
{
//quite know the scrolling should stop, maybe it would be better when the scrollView scrolls even if the user does nothing when the subview is over the visible area
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
break;
}
default:
{
[scrollTimer invalidate];
scrollTimer = nil;
scrolling = NO;
break;
}
}
}
-(void)autoScroll:(NSTimer*)timer {
scrolling = YES; //the scroll method is executed quite know
NSDictionary *info = [timer userInfo];
UIScrollView *scrollView = self.myScrollView;
CGRect visibleRect;
visibleRect.origin = scrollView.contentOffset;
visibleRect.size = scrollView.bounds.size;
CGPoint scrollAmount = CGPointFromString([info objectForKey:#"scrollString"]);
MyImageView *currentView = [info objectForKey:#"currentView"];
//stop scrolling when the UIView is at the edge of the containerView (referenced over 'self')
if ((currentView.frame.origin.x <= 0 | currentView.frame.origin.y <= 0) ||
((currentView.frame.origin.x+currentView.frame.size.width) > self.frame.size.width | (currentView.frame.origin.y+currentView.frame.size.height) > self.frame.size.height)
) {
scrolling = NO;
return;
}
//move the UIView
CGFloat scale = 1.0 / scrollView.zoomScale;
if (scrollAmount.x != 0) {
scrollAmount.x *= scale;
}
if (scrollAmount.y != 0) {
scrollAmount.y *= scale;
}
CGRect frame = currentView.frame;
frame.origin.x += scrollAmount.x;
frame.origin.y += scrollAmount.y;
currentView.frame = frame;
currentFrameOriginX = currentView.frame.origin.x;
currentFrameOriginY = currentView.frame.origin.y;
//move the scrollView
CGPoint contentOffset = scrollView.contentOffset;
contentOffset.x += scrollAmount.x;
contentOffset.y += scrollAmount.y;
[scrollView setContentOffset:contentOffset animated:NO];
}
In my app I have a scrollview and when I press a button it is hidden when I press again it shows up.
I use scrollview.hidden = YES (or NO) to do it.
But I want to do it with an animation. For example, it may disappear from the bottom of the screen by moving and shows up with the same way. How can I do that?
edit:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = bottomScroller.frame;
if(Frame.origin.y == 380){
Frame.origin.y = 460;
}else{
Frame.origin.y = 380;
}
bottomScroller.frame = Frame;
[UIView commitAnimations];
This solved my problem...
You may check UIView animations. It's pretty easy. For example to animate translation you may use something like:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = yourView.frame;
Frame.origin.y = 0;
yourView.frame = Frame;
[UIView commitAnimations];
Then to move it back:
[UIView beginAnimations:#"myAnimation" context:nil];
CGRect Frame = yourView.frame;
Frame.origin.y = 100;
yourView.frame = Frame;
[UIView commitAnimations];
Changes in frame, alpha and some other parameters will be automatically animated.
You can use animations like so:-
[UIView animateWithDuration:2.0 animations:^{
[scrollview setframe:CGRectMake(xpos, ypos, width, height)];
}];
If you want to slide the scrollview on or off the screen set its y position - the bottom of the view is you want to hide it, the normal y position if you want to show it.
The 2.0 is an animation length, this can be changed to whatever you need it to be!
You can do this by using simple view animations. Here's an example of how you might do this:
// myScrollView is your scroll view
-(void)toggleShow{
CGFloat targetAlpha = myScrollView.alpha == 1 ? 0 : 1;
CGFloat yPosition = targetAlpha == 1 ? 0 : self.view.frame.size.height;
[UIView animateWithDuration:1.0 animations:^{
myScrollView.frame = CGRectMake(myScrollView.frame.origin.x, yPosition, myScrollView.frame.size.width, myScrollView.frame.size.height);
myScrollView.alpha = targetAlpha;
}];
}
targetAlpha will always be the opposite of the current state (1 or 0), and if it's 0 then the y position will be set to the bottom of the parent view. Using the new-school UIView animations API with blocks we can execute these changes to the scroll view over 1 second (in my example).
ScrollView appers from down side of screen with animation. I think this up-animation is what you want.
// ScrollView appearing animation
- (void)ScrollViewUpAnimation
{
// put the scroll view out of screen
CGRect frame = ScrollView.frame;
[self.view addSubview:ScrollView];
ScrollView.frame = CGRectMake(0, 460, frame.size.width, frame.size.height);
// setting for animation
CGPoint fromPt = ScrollView.layer.position;
CGPoint toPt = CGPointMake(fromPt.x, fromPt.y - frame.size.height - 44);
CABasicAnimation* anime =
[CABasicAnimation animationWithKeyPath:#"position"];
anime.duration = 0.2;
anime.fromValue = [NSValue valueWithCGPoint:fromPt];
anime.toValue = [NSValue valueWithCGPoint:toPt];
// change the position of Scroll View when animation start
[ScrollView.layer addAnimation:anime forKey:#"animatePosition"];
ScrollView.layer.position = toPt;
}