Show progress line over existing elements - ios

Not sure if the title is very clear but I don't know the exact name for the thing I'm looking for. I have a gird that consists of UIButtons (basically a sequencer). I want a line similar to the vertical line in this image (between the 8th and 9th button of each row:
So basically when my grid is being played I want to indicate the progress. Any idea how I could do this? What kind of elements to use etc.

You can add a UIView that is very narrow, 1 or 1.5 pixels. Set the background color to white or a gradient. Add this view to the same subview as the buttons.
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(100.0, 0.0, 1.5, 320.0)];
lineView.backgroundColor = [UIColor whiteColor];
[parentView addSubview:lineView];
To show progress you'll need to know how much is left until completion. Take that value and divide by 320 to get the lineView height.
CGRect frame = CGRectMake(100.0, 0.0, 1.5, 0);
lineView.frame = frame; // 0% complete
frame = CGRectMake(100.0, 0.0, 1.5, 160.0);
lineView.frame = frame; // 50% complete
frame = CGRectMake(100.0, 0.0, 1.5, 320.0);
lineView.frame = frame; // 100% complete
That is the idea. Hope this help.
Update to vertical bar moving across the screen
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 1.5, 320.0)];
lineView.backgroundColor = [UIColor whiteColor];
[parentView addSubview:lineView];
As progress is made
- (void)progressWithValue(CGFloat)progress {
CGRect frame = lineView.frame;
frame.origin.x = progress; // Change x position
lineView.frame = frame;
}

You can use UISlider to show the progress of your events or activity(not so clear of your code) and customize it according to your needs.You can look at the UICatalog sample code by Apple to get to know-how of customizing a UISlider along with other elements or UISlider Tutorial

Related

iOS gradient fill in text animation

In iOS Objective C, I need the text fill animation with gradient, the animation will fill the gradient from bottom to top (Vertically). I am not good in core graphics or animation things in iOS.
Has anyone did this kind of task before.
Any help would be really appreciated.
Here is an idea. Use two labels and animate the second one in. This has its limits, to go further you need to either use the labels' layers or to convert it to UIImage's and then animate that in. This animates from the center out which is not entirely what you are looking for, but just an idea for now.
The storyboard contains just one button which you need to wire up to kick off the animation.
EDIT
Previously it animated from the center outwards but I've changed it to animate from the bottom to the top.
- ( void ) viewDidLoad
{
[super viewDidLoad];
// Start label
UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake ( 10, 50, 100, 20 )];
// Configure
label.text = #"Label";
label.textColor = UIColor.redColor;
label.sizeToFit;
[self.view addSubview:label];
// The label we animate in
UILabel * label1 = [[UILabel alloc] initWithFrame:CGRectMake ( 10, 50, 100, 20 )];
// Configure
label1.text = #"Label";
label1.textColor = UIColor.blueColor;
label1.clipsToBounds = YES;
label1.contentMode = UIViewContentModeBottom;
label1.sizeToFit;
// We keep a weak reference to it
[self.view addSubview:label1];
self.label1 = label1;
}
- ( IBAction ) buttonAction:( id ) sender
{
CGRect f = self.label1.frame;
CGRect b = self.label1.bounds;
// Need to animate both the bounds and the frame's position
self.label1.frame = CGRectMake( f.origin.x, f.origin.y + f.size.height / 2, f.size.width, f.size.height );
self.label1.layer.bounds = CGRectMake( 0, 0, b.size.width, 0 );
[UIView animateWithDuration:2
animations:^{
self.label1.frame = f;
self.label1.layer.bounds = b;
}];
}
Note that you need to add a weak property to the view controller, e.g.
#property (nonatomic,weak) UILabel * label1;
as you need this later to be able to animate.

IOS/Objective-C: Subview of Subview not displaying

I am trying to create a chart where a bar in the form of a UIView displays on top of a background UIView. I'd like both to display on top of the UIView for the whole screen. I have done this before successfully, but while I can get the first view to display, I somehow can't get my code to display the bar. Could it have something to do with setting the color? Or can anyone suggest why the second subview is not displaying.
My code:
//Make background box:
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat graphWidth = screenWidth-40;
CGFloat graphHeight = 160;
CGRect graphBounds =CGRectMake(20, 200, graphWidth, graphHeight);
float tableStartY = graphBounds.origin.y;
UIView *graphBox = [[UIView alloc] initWithFrame:graphBounds];
graphBox.backgroundColor = [UIColor colorWithRed:200.0/255.0 green:200.0/255.0 blue:200.0/255.0 alpha:0.2]; graphBox.layer.borderWidth = 1;
graphBox.layer.borderColor = [UIColor blueColor].CGColor;
//Make Bar
CGFloat barWidth = 20;
CGFloat barHeight = 100;
CGRect aBar = CGRectMake(20, tableStartY+1, barWidth, barHeight);
UIView *barView = [[UIView alloc] initWithFrame:aBar];
barView.backgroundColor = [UIColor blueColor];
barView.layer.borderWidth = 1;
barView.layer.borderColor = [UIColor redColor].CGColor;
// [graphBox addSubview:barView];
[self.view addSubview: graphBox];
If I run the above code, it displays the graphBox. If I add the bar directly to the view as a subView instead of the graphBox, the bar displays. However, if I uncomment out the line shown and add the barView first to the graphBox and then add the graphBox to the view, the barView does not display.
Thanks in advance for any suggestions.
If I understand correctly what you need to do, you should replace
CGRect aBar = CGRectMake(20, tableStartY+1, barWidth, barHeight);
with
CGRect aBar = CGRectMake(20, 1, barWidth, barHeight);
[edit: and obviously uncomment the addSubview line]
Perhaps this is an accident in your posted code, but you have specifically commented out where the barView would be added to the screen.
// [graphBox addSubview:barView];
In addition, as another answer lists, your offset is incorrect if you are adding barView to graphBox. If you add it to self.view instead, your offset is correct.
So, you've got two choices, depending on the containment you desire in your view hierarchy:
CGRect aBar = CGRectMake(20, 1, barWidth, barHeight);
// ...
[graphBox addSubview:barView];
or
CGRect aBar = CGRectMake(20, tableStartY+1, barWidth, barHeight);
// ...
[self.view addSubview: graphBox];
[self.view addSubview:barView];
Note that in the second option, the order is important to get the barView to display over top of the graphBox as they will be siblings.

How to add drop shadow only for the right side?

I am adding the drop shadow like this.
vwVertical=[[UIView alloc] init];
[vwVertical setBackgroundColor:[UIColor whiteColor]];
vwVertical.translatesAutoresizingMaskIntoConstraints = NO;
vwVertical.layer.shadowColor=[UIColor colorWithRed:32/255 green:59/255 blue:90/255 alpha:1.0].CGColor;
vwVertical.layer.shadowOffset=CGSizeMake(5, 0);
vwVertical.layer.shadowOpacity=0.12;
vwVertical.layer.shadowRadius=6.5;
[vwBlock addSubview:vwVertical];
But this is adding shadow 3 sides. How can I add just to right side.
Please help me.
Thanks
All you need to do is to inset the view's bounds on top - left - bottom, and use the shadow path.
vwVertical=[[UIView alloc] init];
// Create edge insets
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 10, 0, 0);
// Create rect with inset and view's bounds
CGRect shadowPathOnlyIncludingRight = UIEdgeInsetsInsetRect(vwVertical.bounds, contentInsets);
// Apply it on the layer's shadowPath property
vwVertical.layer.shadowPath = [UIBezierPath bezierPathWithRect:shadowPathOnlyIncludingRight].CGPath;
[vwVertical setBackgroundColor:[UIColor whiteColor]];
vwVertical.translatesAutoresizingMaskIntoConstraints = NO;
vwVertical.layer.shadowColor=[UIColor colorWithRed:32/255 green:59/255 blue:90/255 alpha:1.0].CGColor;
vwVertical.layer.shadowOffset=CGSizeMake(5, 0);
vwVertical.layer.shadowOpacity=0.12;
vwVertical.layer.shadowRadius=6.5;

Animate 3 lines into arrow in iOS for a uibutton

Does anyone know how to animate 3 parallel lines to a right arrow as happens in navigation menu bar to open up a side menu by applying CGAfflineRotation to 3 views .
I really need help on these as so that at-least I can get an idea to start it.
This is what it will be look like some how tried to draw it:-
_______
_______ \
_______ to ___________\
/
/
Any idea or suggestion will be helpful.
As you said, you should use CGAffineRotation. I'm giving simple example of what you want, everything should be ofc packed into proper methods, views should be with some basic autolayouts/layoutFrames etc. I'm just posting possible solution for rotation, quickly written in viewDidAppear, what should be changed.
Another option is to use eg firstView.layer.anchorPoint and set it to proper position.
#define degreesToRadians(x) (M_PI * x / 180.0)
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// let's create these 3 views as a lines
UIView *firstView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 50, 1)];
[firstView setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:firstView];
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(50, 53, 50, 1)];
[secondView setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:secondView];
UIView *thirdView = [[UIView alloc] initWithFrame:CGRectMake(50, 56, 50, 1)];
[thirdView setBackgroundColor:[UIColor blackColor]];
[self.view addSubview:thirdView];
// now we can perform rotation, mind the degree and offsets in transform.
[UIView animateWithDuration:1 animations:^{
CGAffineTransform transform = CGAffineTransformMakeTranslation(24, 1);
transform = CGAffineTransformRotate(transform, degreesToRadians(45));
transform = CGAffineTransformTranslate(transform, -24, 1);
firstView.transform = transform;
transform = CGAffineTransformIdentity;
transform = CGAffineTransformMakeTranslation(24, -1);
transform = CGAffineTransformRotate(transform, degreesToRadians(-45));
transform = CGAffineTransformTranslate(transform, -24, -1);
thirdView.transform = transform;
} completion:^(BOOL finished) {}];
}
It is easier to do such animation with CALayer Animation instead of UIView Animation. You need to add three sub layers: top_line, middle_line, and bottom_line. Then draw the line on each layer and the right position (Hint: use CAShapeLayer for drawing lines). At last just rotate the top_line and bottom_line layer with a proper angle, you probably need to change the anchor point and layer's position as well. It is quite tricky at the last step, maybe just do some trials with different angle and anchor point values until you find the most proper ones.
When you successfully did such animation, try to do the reverse animation. Then create a Custom UIButton with these two animations embedded in. Congratulations! You have a hamburger button with cool animations which could be reused anywhere!

How to find the center of a UIView after superviews have been transformed?

NOTE: code written in browser; probably not perfectly accurate, but it should give the general idea.
I've got a stack of views, something like this:
CGRect theFrame = CGRectMake(0, 0, 10, 10);
UIView *v1 = [UIView alloc] initWithFrame: theFrame];
UIView *v2 = [UIView alloc] initWithFrame: theFrame];
UIView *v3 = [UIView alloc] initWithFrame: theFrame];
// Set all the background colors, so I can see them: snip
// set all the clipsToBounds = NO, so I can place however I want: snip
v2.center = CGPointMake(100, 80);
[v1 addSubview: v2];
v3.center = CGPointMake (57, 42);
[v2 addSubview: v3];
v1.center = CGPointMake (193, 44);
[self.view addSubview: v1];
// etc., time passes, user presses TEST button
CGAffineTransform sXfrm = CGAffineTransformMakeScale(3.5, 4.7);
CGAffineTransform rXfrm = CGAffineTransformMakeRotation(M_PI / 3.);
v1.transform = CGAffineTransformConcat(sXfrm, rXfrm);
// etc., rotate & scale v2, while we're at it. snip
// Leave v3 unrotated & unscaled.
Ok, at this point, everything is displaying on the screen exactly as desired. My question is:
Where (that is: where, in self.view's coordinate space) is v3.center?
Here's some code that gives the CLOSE answer, but not-quite right, and I can't seem to figure out what's wrong with it:
CGRect testRect = CGRectMake(0, 0, 20, 20);
UIView *testView = [[UIView alloc] initWithFrame: testRect];
testView.backgroundColor = [UIColor greebColor];
[self.view addSubview: testView];
CGPoint center = v1.center;
#if 1 // Apply Transform
CGPoint ctr2 = CGPointApplyAffineTransform(v2.center, v1.transform);
#else // use layer
CGPoint ctr2 = CGPointMake ((v2.layer.frame.origin.x + (v2.layer.frame.size.width / 2.)),
(v2.layer.frame.origin.y + (v2.layer.frame.size.height / 2.)) );
ctr2 = CGPointApplyAffineTransform(ctr2, v1.transform);
#endif
center.x += ctr2.x;
center.y += ctr2.y;
CGPoint ctr3 = CGPointApplyAffineTransform(v3.center, CGAffineTransformConcat(v2.transform, v1.transform));
center.x += ctr3.x;
center.y += ctr3.y;
testView.center = center; // I want this to lay on top of v3
[self.view addSubview: testView];
NOTE: In my actual code, I put in-between test-views at all the intermediate centers. What I get is: testView1 (center) is correct, testView2 (ctr2) is off by a little, testView3 (ctr3) is off by a little more.
The #if is because I was experimenting with using ApplyAffine vs layer. Turns out they both give the same result, but it's a tad off.
Hopefully clarifying image:
You can use the UIView's convert points methods:
convertPoint:toView:
convertPoint:fromView:
convertRect:toView:
convertRect:fromView:
However once you apply a transformation, you should stop using frames and use center + bounds instead (which might be the reason your code is not working), from apple docs (for frame):
Warning: If the transform property is not the identity transform, the
value of this property is undefined and therefore should be ignored.
The only other thing you have to be conscious about is, which view invoques the convert to what other view since the results change. (source coordinate system -> target coordinate system.)
EDIT:
Thanks to this answer and Chiquis' comments in the question, my (Olie's) final (working) code looks like this:
CGRect testRect = CGRectMake(0, 0, 20, 20);
UIView *testView[3];
for (int ii = 0 ; ii < 3 ; ++ii)
{
testView[ii] = [[UIView alloc] initWithFrame: testRect];
testView[ii].backgroundColor = [UIColor redColor];
[self.view addSubview: testView[ii]];
}
testView[0].center = v0.center;
testView[1].center = [v0 convertPoint: v1.center toView: self.view];
testView[2].center = [v1 convertPoint: v2.center toView: self.view];
To clarify some, v1 is a subview of v0, and v2 is a subview of v1; this dictates the receivers in the covertPoint: calls.
I try to let iOS do the heavy lifting for me when I need to transform points between different layers.
CGPoint point = v3.center;
CGPoint ctr3 = [v3.layer.presentationLayer convertPoint:point toLayer:self.layer.presentationLayer];
The presentation layer object represents the state of the layer as it currently appears onscreen. This can help avoid timing issues if animations are involved.
Just noticed the comment that came in while I was composing: yes, I use this to account for rotation transforms similar to what you describe.

Resources