iOS UITextView Always scroll to bottom - ios

I have this scenario: when I click on a UITextView, it will either expand or shrink. But every time the UITextView shrinks, the content scroll down to the bottom.
I've tried
[textView setContentOffset:CGPointZero animated:YES];
textView.scrollsToTop = YES;
But none of them works.
Here is the screenshot:
Here is the code:
- (IBAction)tapText:(id)sender {
NSLog(#"tap text view");
[self addBoundsSpringAnimationToView:self.movieTextView];
}
- (void) addBoundsSpringAnimationToView:(UITextView *)textView {
POPSpringAnimation *anim = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerBounds];
if (expand) {
NSLog(#"expand");
self.bgUIImageView.hidden = YES;
self.blurredImageView.hidden = NO;
anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0.0f, 320.0f, 807.0f)];
anim.springBounciness = 18;
} else {
NSLog(#"Shrink");
self.bgUIImageView.hidden = NO;
self.blurredImageView.hidden = YES;
anim.toValue = [NSValue valueWithCGRect:CGRectMake(0, 355.0f, 320.0f, 225.0f)];
}
expand = !expand;
anim.springSpeed = 10;
[textView pop_addAnimation:anim forKey:#"popBounds"];
}
Is it related to the Facebook Pop animation?
Thanks!

Related

iOS Layer animation with auto layout

I have a simple container view(green) and two sub views(red and blue) as below.
The container view is not applying auto layout and I config its size & location by frame.
While the sub views are applying auto layout(see code below)
#implementation XXView {
UIView *_leftView;
UIView *_rightView;
}
- (instancetype)init {
self = [super initWithFrame:CGRectZero];
if (self) {
[self setupViewHierarchy];
[self setupConstraints];
}
return self;
}
- (void)setupViewHierarchy {
_leftView = [[UIView alloc] init];
_leftView.backgroundColor = UIColor.redColor;
_leftView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_leftView];
_rightView = [[UIView alloc] init];
_rightView.backgroundColor = UIColor.blueColor;
_rightView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_rightView];
self.backgroundColor = UIColor.greenColor;
}
- (void)setupConstraints {
[NSLayoutConstraint activateConstraints:#[
[_leftView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10],
[_leftView.topAnchor constraintEqualToAnchor:self.topAnchor],
[_leftView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
[_leftView.widthAnchor constraintEqualToConstant:50],
[_rightView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-10],
[_rightView.topAnchor constraintEqualToAnchor:_leftView.topAnchor],
[_rightView.bottomAnchor constraintEqualToAnchor:_leftView.bottomAnchor],
[_rightView.widthAnchor constraintEqualToAnchor:_leftView.widthAnchor],
]];
}
...
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
XXView *tv = [[XXView alloc] init];
CGFloat width = self.view.bounds.size.width;
tv.frame = CGRectMake(50, 400, width-100, 100);
[self.view addSubview:tv];
self.tv = tv;
}
Then I would like to animate the container's width change by using the CABasicAnimation as below:
- (void)startAnimation {
[CATransaction begin];
CGFloat width = self.bounds.size.width;
CABasicAnimation *widthAnimation = [CABasicAnimation animationWithKeyPath:#"bounds.size.width"];
widthAnimation.fromValue = #(width/2);
widthAnimation.toValue = #(width);
widthAnimation.duration = 1;
[self.layer addAnimation:widthAnimation forKey:#"123"];
[CATransaction commit];
}
However, the animation is not as I would expect. I would expect the left view moves as the container's leading side and the right view does as the trailing side.
What I see is, the green view expands as expected and the left view moves as green view's leading side. However, the right view is always keeping the same distance to left view. Below is the screenshot taken at the beginning of the animation.
Why the animation is not working as expected?
The problem is that your CABasicAnimation is modifying the bounds of the layer ... but as far as auto-layout is concerned that does not change the width of the view.
It's not really clear what your goal is here, but if you change your animation method to this it might get you on your way:
- (void)startAnimation {
CGRect f = self.frame;
CGFloat fw = f.size.width;
f.size.width = fw / 2;
self.frame = f;
[self layoutIfNeeded];
f.size.width = fw;
[UIView animateWithDuration:1.0 animations:^{
self.frame = f;
[self layoutIfNeeded];
}];
}

Removing a subview of UICollectionView cell content view crashes if that subview has a animated layer

I have added CABasicAnimation on a layer which is added as sublayer to a custom view.Custom view is then added to collection view cell content view as subview. I want to show indefinite progress like bar moving from left to right until download completes(please refer the attachments). Everything is fine except, once download is completed,For removing the custom view with layer animation, i cannot access that view using the tag. The compiler crashes and throws 'could not load any Objective-C class information. This will significantly reduce the quality of type information available'
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableDictionary *attachDetail = [self getManagedObjectForIndexPath:indexPath];
ZCRMMailAttachmentCollectionViewCell *cell = (ZCRMMailAttachmentCollectionViewCell*)[collectionView dequeueReusableCellWithReuseIdentifier:#"cellId" forIndexPath:indexPath];
[self setVisibilityOfRemoveButtonInCell:cell inObj:attachDetail];
[self setVisibilityOfSizeInCell:cell inObj:attachDetail];
cell.attachDetails = attachDetail;
[self addLoadingViewIfNotDownloaded:cell managedObject:attachDetail];
[cell redraw];
return cell;
}
-(void)addLoadingViewIfNotDownloaded:(ZCRMMailAttachmentCollectionViewCell*)cell managedObject:(NSDictionary*)attachDict
{
NSNumber *progress = attachDict[MAIL_ATTACH_DOWNLOAD_PROGRESS];
if(!ZV_IsEmpty(progress))
{
if (isinf([progress floatValue]))
{
[cell addLinearInfiniteProgress];
}
else
{
[cell removeLinearProgress];
}
}
}
-(void)addLinearInfiniteProgress
{
UIView* view = [zTvContentView viewWithTag:1234];
if(!view)
{
UIView *progressView = [UIView new];
progressView.tag = 1234;
progressView.layer.masksToBounds = YES;
[zTvContentView addSubview:progressView];
[progressView setBottom:-1];
[progressView alignTo:VTLayoutAlignLeft|VTLayoutAlignRight];
[progressView setHeight:2];
//ProgressLayer
CGRect frame = self.contentView.bounds;
CALayer * indeterminateLayer = [CALayer layer];
indeterminateLayer.delegate = self;
indeterminateLayer.name = #"progresslayer";
indeterminateLayer.frame = CGRectMake(0, 0, 0.4*CGRectGetWidth(frame), 3);
indeterminateLayer.backgroundColor = [CurTheme() themeActionItemColor].CGColor;
indeterminateLayer.opacity = 1;
[progressView.layer addSublayer:indeterminateLayer];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.duration = 1;
animation.repeatCount = HUGE_VALF;
animation.removedOnCompletion = YES;
animation.fromValue = [NSValue valueWithCGPoint:CGPointMake( CGRectGetMinX(frame) - indeterminateLayer.frame.size.width + OFFSET, 0)];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(frame) + indeterminateLayer.frame.size.width - OFFSET, 0)];
[indeterminateLayer addAnimation:animation forKey:#"position"];
}
}
(void)removeLinearInfiniteProgress
{
UIView* progressView = [zTvContentView viewWithTag:1234];
if(progressView)
{
if ([progressView superview])
{
[progressView removeFromSuperview];
}
}
}
Any help would be appreciated.

UILabel auto scroll like a marquee

I am trying I make a UI label auto scroll the text that is inside it like a marquee. The below code works depending on what kind of text/content I throw at it. I can't tell what works and what does not. What happens is that the label does not show anything, then suddenly scrolls the text at an extremely high speed that you can't read it.
For some reason, this works perfectly on the iPhone but not on the iPad. I am guessing because the text I am putting at the iPhone is always way bigger than the Uilabel's size?
Here is the code:
self.currentTrack.textColor = [UIColor colorWithRed:15.0/255.0 green:176.0/255.0 blue:223.0/255.0 alpha:1];
self.currentTrack.labelSpacing = 35; // distance between start and end labels
self.currentTrack.pauseInterval = 1.7; // seconds of pause before scrolling starts again
self.currentTrack.scrollSpeed = 30; // pixels per second
self.currentTrack.textAlignment = NSTextAlignmentCenter; // centers text when no auto-scrolling is applied
self.currentTrack.fadeLength = 12.f;
self.currentTrack.scrollDirection = CBAutoScrollDirectionLeft;
[self.currentTrack observeApplicationNotifications];
}
Here is the code:
AutoScrollLabel, extended from UIView (code provided below) has an instance method to set text (setLabelText). The text provided will auto scroll only if the length of the text is bigger than the width of the AutoScrollLabel view.
Eg:
AutoScrollLabel *autoScrollLabel = [[AutoScrollLabel alloc] initWithFrame:CGRectMake(0, 0, 150, 25)];
[autoScrollLabel setLabelText:#"Some lengthy text to be scrolled"];
Note: You can make changes in setLabelText: Method implementation to support Attributed Text (use appropriate methods to find size of Attributed Text)
*** AutoScrollLabel.h
#interface AutoScrollLabel : UIView {
UILabel *textLabel;
NSTimer *timer;
}
- (void) setLabelText:(NSString*) text;
- (void) setLabelFont:(UIFont*)font;
- (void) setLabelTextColor:(UIColor*)color;
- (void) setLabelTextAlignment:(UITextAlignment)alignment;
#end
**** AutoScrollLabel.m
#import "AutoScrollLabel.h"
#import <QuartzCore/QuartzCore.h>
#implementation AutoScrollLabel
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.clipsToBounds = YES;
self.autoresizesSubviews = YES;
textLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
textLabel.backgroundColor = [UIColor clearColor];
textLabel.textColor = [UIColor blackColor];
textLabel.textAlignment = UITextAlignmentRight;
[self addSubview:textLabel];
}
return self;
}
-(void) setLabelText:(NSString*) text {
textLabel.text = text;
CGSize textSize = [text sizeWithFont:textLabel.font];
if(textSize.width > self.frame.size.width) {
textLabel.frame = CGRectMake(0, 0, textSize.width, self.frame.size.height);
}
else {
textLabel.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
if(textLabel.frame.size.width > self.frame.size.width) {
[timer invalidate];
timer = nil;
CGRect frame = textLabel.frame;
frame.origin.x = self.frame.size.width-50;
textLabel.frame = frame;
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(moveText) userInfo:nil repeats:YES];
}
else {
[timer invalidate];
timer = nil;
}
}
-(void) moveText {
if(textLabel.frame.origin.x < textLabel.frame.size.width-2*textLabel.frame.size.width) {
CGRect frame = textLabel.frame;
frame.origin.x = self.frame.size.width;
textLabel.frame = frame;
}
[UIView beginAnimations:nil context:nil];
CGRect frame = textLabel.frame;
frame.origin.x -= 5;
textLabel.frame = frame;
[UIView commitAnimations];
}
- (void) setLabelFont:(UIFont*)font {
textLabel.font = font;
}
- (void) setLabelTextColor:(UIColor*)color {
textLabel.textColor = color;
}
- (void) setLabelTextAlignment:(UITextAlignment)alignment {
textLabel.textAlignment = alignment;
}
For Swift5
set top of your UILabel equal to superview bottom, and then use this code:
func createAnimationLbl() -> CAAnimation {
let animation = CABasicAnimation(keyPath: "transform.translation.y")
animation.fromValue = 0
animation.toValue = -(self.view.frame.height )
animation.duration = 15
animation.repeatCount = Float.infinity //times of scrolling, infinity for unlimited times.
animation.timingFunction = CAMediaTimingFunction.init(name: CAMediaTimingFunctionName.linear)
animation.fillMode = CAMediaTimingFillMode.forwards
animation.isRemovedOnCompletion = false
return animation
}
then in your viewWillAppear call it as below:
self.aboutLbl.layer.add(createAnimationLbl(), forKey: nil)

Animating image on event

I have an image with circle inside circle approx 10 circles,each of different color i want to show them one by one on button click from center to outside and on another button click disappear one by one from outer to inner. Is this possible and how?
Please suggest
You can get help from below code i m not wrong and you are looking for radar style animation
#import "RadarAnimation.h"
#implementation RadarAnimation
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self SetUPAnimation];
// Initialization code
}
return self;
}
-(void)SetUPAnimation
{
[self AddRader];
double delayInSeconds = 1.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self AddRader];
});
}
-(void)AddRader
{
UIView *view = [[UIView alloc] init];
// view.backgroundColor = [UIColor blueColor];
view.frame=self.frame;
[self setWidth:[Helper currentWidth] ofView:view];
[self setHeight:[Helper currentWidth] ofView:view];
[self setXPossitionto:self.frame.size.width/2-view.frame.size.width/2 ofView:view];
[self setYPossitionto:self.frame.size.height/2-view.frame.size.height/2 ofView:view];
view.layer.cornerRadius = CGRectGetHeight(view.frame)/2;
view.tag=175751;
int kRingExpansionTime=6.0;
CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:#"transform.scale"];
scaleAnimation.duration = kRingExpansionTime;
scaleAnimation.repeatCount = HUGE_VAL;
scaleAnimation.autoreverses = YES;
scaleAnimation.fromValue = [NSNumber numberWithFloat:0.0];
scaleAnimation.toValue = [NSNumber numberWithFloat:1.8];
[view.layer addAnimation:scaleAnimation forKey:#"scale"];
// fade out the ring part way thru the animation
CABasicAnimation* alphaAnim = [CABasicAnimation animationWithKeyPath:#"opacity"];
alphaAnim.fromValue = [NSNumber numberWithFloat:0.3];
alphaAnim.toValue = [NSNumber numberWithFloat:0.1];
alphaAnim.beginTime = kRingExpansionTime * 0.7f; // start part way thru
alphaAnim.duration = kRingExpansionTime - alphaAnim.beginTime;
CAAnimationGroup* group = [CAAnimationGroup animation];
group.duration = kRingExpansionTime;
group.repeatCount = HUGE_VALF; // repeat forever
group.animations = [NSArray arrayWithObjects:scaleAnimation,alphaAnim, nil];
[view.layer addAnimation:group forKey:nil];
view.backgroundColor = [UIColor colorWithRed:0/255.0 green:135.0/255.0 blue:219.0/255.0 alpha:0.5];
view.alpha=0.4;
view.layer.borderColor = [UIColor colorWithRed:0/255.0 green:135.0/255.0 blue:255.0/255.0 alpha:1.0].CGColor;
view.layer.borderWidth = 2.0;
//view.center=_VwEmptyplaceholder.center;
//view.center=CGPointMake(_VwEmptyplaceholder.frame.size.width/2, _VwEmptyplaceholder.frame.size.height/2);
view.userInteractionEnabled=NO;
view.tag=175751;
//[self insertSubview:view belowSubview:btn];
}
-(void)setXPossitionto:(CGFloat)xpostion ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.origin.x = xpostion;
view.frame=frame;
}
-(void)setYPossitionto:(CGFloat)ypostion ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.origin.y = ypostion;
view.frame=frame;
}
-(void)setWidth:(CGFloat)Width ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.size.width = Width;
view.frame=frame;
}
-(void)setHeight:(CGFloat)Height ofView:(UIView *)view
{
CGRect frame = view.frame;
frame.size.height = Height;
view.frame=frame;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end

View stops animation after navigation

I create my custom loading indicator. So, I update my view like this
#property (nonatomic, readonly) CAShapeLayer *progressLayer;
#property (nonatomic, readwrite) BOOL isAnimating;
- (void)layoutSubviews {
[super layoutSubviews];
self.progressLayer.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(self.bounds));
[self updatePath];
}
- (void)updatePath {
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGFloat radius = MIN(CGRectGetWidth(self.bounds) / 2, CGRectGetHeight(self.bounds) / 2) - self.progressLayer.lineWidth / 2;
CGFloat startAngle = 3 * M_PI_2;
CGFloat endAngle = self.endAngle;
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
self.progressLayer.path = path.CGPath;
}
Animation starts here
- (void)startAnimating {
if (self.isAnimating)
return;
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = #"transform.rotation";
animation.duration = 1.0f;
animation.fromValue = #(0.0f);
animation.toValue = #(2 * M_PI);
animation.repeatCount = INFINITY;
[self.progressLayer addAnimation:animation forKey:kLLARingSpinnerAnimationKey];
self.isAnimating = true;
}
- (void)stopAnimating {
if (!self.isAnimating)
return;
[self.progressLayer removeAnimationForKey:kLLARingSpinnerAnimationKey];
self.isAnimating = false;
}
- (CAShapeLayer *)progressLayer {
if (!_progressLayer) {
_progressLayer = [CAShapeLayer layer];
_progressLayer.strokeColor = self.tintColor.CGColor;
_progressLayer.fillColor = nil;
_progressLayer.lineWidth = 1.5f;
}
return _progressLayer;
}
I have a tab bar app, so the problem is when animation start, I click to another tab and then return to first view, my custom indicator doesn't animate. Also this bug(feature) appear in collection view cells if I add my indicator to cells.
I add my indicator by calling simple method addSubview:
[view addSubview:indicator];
[indicator startAnimating];
How can I prevent stop animating after navigation?
Thanks in advance.
Here you go,
//I have used a BOOL viewChange to check if spinner was running or not while user tapped to different view in tabBar.
- (void)viewWillAppear:(BOOL)animated{ //On initial run the viewChange will be false and the condition in if won't run but as spinnerView start animating u could change the viewChange flag to true.
//So when user will be back from second view then this flag would be true and it will start animating.
//As your work is complete then just stop animating and remove from view.
if(viewChange){
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil;
spinnerView = [[LLARingSpinnerView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 40, 40)];
[self.view addSubview:spinnerView];
[spinnerView startAnimating];
}
}
- (void)viewWillDisappear:(BOOL)animated{
//This is called when user move from current view to another and at that point we user won't be seeing the spinnerView so remove it and release it only if it's animating.
if(spinnerView.isAnimating){
viewChange = YES;
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil;
}
else{
viewChange = NO;
}
}
- (IBAction)stopBtn:(id)sender{ //Spinner is stop animating with tap on button and is removed from the view.
if(spinnerView.isAnimating){
[spinnerView stopAnimating];
[spinnerView removeFromSuperview];
spinnerView = nil; //release the obj of spinnerView.
}
}
- (IBAction)btnTap:(id)sender{ //Spinner is start animating with tap on button
if(!spinnerView)
spinnerView = [[LLARingSpinnerView alloc] initWithFrame:CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 40, 40)];
spinnerView.lineWidth = 1.5f;
spinnerView.tintColor = [UIColor redColor];
[self.view addSubview:spinnerView];
[spinnerView startAnimating];
}
Hope this helps out and if any doubt do let me know. Also if possible u should avoid user to move to another view when spinnerView is running as it indicates that some sort of service or update is running and at that point user should move to another view till it's finish.

Resources