I try show checkmark(GitHub WVCheckMark) when i tap on button. Here is code, and it work only when i tap on button second time.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
mark = [[WVCheckMark alloc] init];//WithFrame:CGRectMake(50, 50, 100, 100)];
mark.backgroundColor = [UIColor clearColor];
//[mark setFrame:CGRectMake(100, 100, 100, 100)];
//[self.view addSubview:mark];
}
- (IBAction) btnShow{
[mark setFrame:CGRectMake(100, 100, 100, 100)];
[self.view addSubview:mark];
[mark setNeedsDisplay];
[mark updateConstraints];
[mark setNeedsLayout];
[mark start];
}
It's work how i want (tap and show) if use addSubview in viewDidLoad.
Calling this methods don't help.
[mark setNeedsDisplay];
[mark updateConstraints];
[mark setNeedsLayout];
Is it possible add subview and show it in one place of code?
Full code
#import "ViewController.h"
#import "TestCheckmark-Swift.h"
#interface ViewController ()
#end
WVCheckMark *mark;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
mark = [[WVCheckMark alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];
mark.backgroundColor = [UIColor clearColor];
[mark setFrame:CGRectMake(100, 100, 100, 100)];
//[self.view addSubview:mark];
}
- (IBAction) btnShow{
//[mark setFrame:CGRectMake(100, 100, 100, 100)];
//[self.view addSubview:mark];
[mark setNeedsDisplay];
[mark updateConstraints];
[mark setNeedsLayout];
[mark start];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Short answer: the OP code doesn't need to call setNeedsDisplay, setNeedsLayout, updateConstraints on the added view, but it does need to call addSubview: on the parent view, as in [self.view addSubview:mark];
But to go a little deeper, it looks like WVCheckMark on GitHub is a subclass of UIView. That's a good, important thing because it means we can probably count on some desirable stuff inherited from UIView.
For example, the .alpha property, which is a good way to alter visibility without messing around with the view hierarchy (adding and removing child views). Another nice thing about .alpha is that -- unlike .hidden -- alpha can be animated.
So let's setup the view hierarchy and framing in one place. The key idea -- and the idea probably most confounding the OP code -- is to call addSubview:. Doing the setup lazily is a good way to insure we do it exactly once, at the point we need to.
- (WVCheckMark *)mark {
WVCheckMark *mark = [self.view viewWithTag:64]; // 64 must be non-zero and unique amongst self.view subviews
if (!mark) {
mark = [[WVCheckMark alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
mark.tag = 64; // so we can find it later
mark.backgroundColor = [UIColor clearColor];
// start out invisible
mark.alpha = 0.0;
// important, addSubview, which is probably the main problem in the OP
[self.view addSubview:mark];
}
return mark;
}
Now, whenever you need to show/hide that view, just do this:
self.mark.alpha = 1.0; // show
self.mark.alpha = 0.0; // hide
Since that property is animatable, you can get a little fancy fade like this:
- (void)setMarkHidden:(BOOL)hidden animated:(BOOL)animated {
NSTimeInterval duration = (animated)? 0.3 : 0.0;
CGFloat alpha = (hidden)? 0.0 : 1.0;
[UIView animateWithDuration:duration animations:^{
self.mark.alpha = alpha;
}];
}
You can throw away all of your other check mark related code, and just do this when the button is pressed to trigger the appearance...
- (IBAction)pressed:(id)sender {
[setMarkHidden:YES animated:YES];
}
Related
I'm trying to figure out an approach to build something like the image below, which is a list of items that when a section is clicked slides out content. It's a really common UX on most websites and what not. My idea is to have each gray box (button) slide out a UIView containing some other items. I'm still new to iOS development but I'm struggling to find how you can animate a UIView to slide down and push the content below it down as well. Hoping some one can give me a good starting point or point to some info outside the realm of the apple docs.
Thanks!
So if you just have a few views, I would not recommend the UITableView approach, since it is not so easy to customize with animations and table views usually want to fill the whole screen with cells. Instead write a expandable UIView subclass that has the desired two states. Add a method to switch between extended and collapsed state. On expanding/collapsing adjust their positions so that they always have enough space.
I provide you an example of views adjusting their frames. I guess it should be easy to do the same with auto layout constraints: give the views a fixed height constraint and change this on collapsing/expanding. The same way set the constraints between the views to be 0 so that they are stacked on top of each other.
Expandable View:
#interface ExpandingView(){
UIView *_expandedView;
UIView *_seperatorView;
BOOL _expanded;
}
#end
#implementation ExpandingView
- (id)init
{
self = [super initWithFrame:CGRectMake(15, 0, 290, 50)];
if (self) {
_expanded = NO;
self.clipsToBounds = YES;
_headerView = [[UIView alloc] initWithFrame:self.bounds];
_headerView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self addSubview:_headerView];
_seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-1, self.bounds.size.width, 1)];
_seperatorView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:_seperatorView];
_expandedView = [[UIView alloc] initWithFrame:CGRectOffset(self.bounds, 0, self.bounds.size.height)];
_expandedView.backgroundColor = [UIColor blueColor];
[self addSubview:_expandedView];
}
return self;
}
- (void)layoutSubviews{
[self adjustLayout];
}
- (void)adjustLayout{
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, 50);
_seperatorView.frame = CGRectMake(0, 49, self.bounds.size.width, 1);
_expandedView.frame = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height-50);
}
- (void)toggleExpandedState{
_expanded = !_expanded;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _expanded?200:50);
[self adjustLayout];
}
#end
ViewController:
#interface ExpandingViewController (){
NSArray *_expandingViews;
}
#end
#implementation ExpandingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_expandingViews = #[
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
];
for(ExpandingView *view in _expandingViews){
[view.headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(expandingViewTapped:)]];
[self.view addSubview:view];
}
}
- (void)viewWillLayoutSubviews{
int y = 100;
for(ExpandingView *view in _expandingViews){
view.frame = CGRectOffset(view.bounds, (CGRectGetWidth(self.view.bounds)-CGRectGetWidth(view.bounds))/2, y);
y+=view.frame.size.height;
}
}
- (void)expandingViewTapped:(UITapGestureRecognizer*)tapper{
ExpandingView *view = (ExpandingView*)tapper.view.superview;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:0 animations:^{
[view toggleExpandedState];
[self.view layoutIfNeeded];
} completion:nil];
}
I’m adding a UISegmentedControl right under the NavigationBar in a UITableViewController. This is the code.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationBar = self.navigationController.navigationBar;
UIView *segmentView=[[UIView alloc] initWithFrame:CGRectMake(0, self.navigationBar.frame.size.height, self.navigationBar.frame.size.width, 50)];
[segmentView setBackgroundColor:[UIColor whiteColor]];
segmentView.alpha = 0.95;
self.tabSegmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Favourites", #"All", nil]];
self.tabSegmentedControl.frame = CGRectMake(20, 10, self.navigationBar.frame.size.width - 40, 30);
[self.tabSegmentedControl addTarget:self action:#selector(tabChanged:) forControlEvents:UIControlEventValueChanged];
[segmentView addSubview:self.tabSegmentedControl];
[self.navigationBar addSubview:segmentView];
[self.tabSegmentedControl setSelectedSegmentIndex:1];
}
The view and the SegmentedControl appear on the screen well, but they are not clickable. The selector doesn’t get executed when tapped on the SegmentControl; it doesn’t even switch tabs! In fact, the stuff that is underneath the segmentView (items in the TableView) get clicked when you tap on it. I have tried but failed to understand why this is happening! Any suggestions would be helpful!
You are adding a view below the bounds of its super view. You may see the view however you cannot click it because it is out of bounds. If you set the property of the navigation bar clipsToBounds to YES you should see that the view disappears. What you need to do is add the segment controller to the table view. Here is an example:
- (void)viewDidLoad
{
[super viewDidLoad];
...
[self.view addSubview: self.segmentView]; // need to keep a pointer to segmentView
self.tableView.contentInset = UIEdgeInset(self.segmentView.frame.size.height, 0,0,0);
}
-(void) scrollViewDidScroll:(UIScrollView*) scrollView{
CGRect rect = self.segmentView.frame;
rect.origin = self.tableView.contentOffset;
self.segmentView.frame = rect;
}
I want to center a subview in the middle of its parent, I have looked at answers on SO but so far they have not helped me. This one specifically looks like it should work, but doesn't.
Here is what I am doing
-(void)viewDidLoad {
[super viewDidLoad];
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
finalNumberCircle.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
finalNumberCircle.Color = [UIColor darkGrayColor];
[self.view addSubview:finalNumberCircle];
}
I also tried the following:
-(void)viewDidLoad {
[super viewDidLoad];
CGPoint translatedP = [self.view convertPoint:self.view.center fromView:self.view.superview];
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 100, 100];
finalNumberCircle.center = translatedP;
[self.view addSubView:finalNumberCircle];
}
Here is how it looks (it's the grey circle)
You have to move your code (2. example) from viewDidLoad to viewWillLayoutSubviews and it will work as suspected.
Before viewWillLayoutSubview is called your viewcontroller view bounds still can change.
convertPoint and related methods used when you want to convert coordinates from one view's coordinates to another, that's not your situation. You have to do something like:
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
finalNumberCircle.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
[self.view addSubview:finalNumberCircle];
Try below code...
[self.view addSubView:finalNumberCircle];
finalNumberCircle.frame=CGRectMake(finalNumberCircle.frame.origin.x+(self.view.frame.size.width/2-finalNumberCircle.center.x), finalNumberCircle.frame.origin.y+(self.view.frame.size.height/2-finalNumberCircle.center.y), finalNumberCircle.frame.size.width, finalNumberCircle.frame.size.height);
Another possibility how to do it (in case You need specific offset from sides). Just provide an offset from parent view sides, and it will auto-resize itself in center of parent view, and will also autoresize, in case parent view frame is changed.
- (void)viewDidLoad
{
[super viewDidLoad];
CGFloat mOffsetFromSide = 50;
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(self.view.bounds.origin.x+mOffsetFromSide, self.view.bounds.origin.y+mOffsetFromSide, self.view.bounds.size.width-mOffsetFromSide*2, self.view.bounds.size.height-mOffsetFromSide*2];
[finalNumberCircle setAutoresizingMask:(UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth)];
[self.view addSubView:finalNumberCircle];
}
In case You simply want to position view in it's parent view's center, leaving it's frame intact, then:
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
finalNumberCircle.center = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
[self.view addSubview:finalNumberCircle];
And yes - it is exact copy of Your provided code. It works on my side.
Maybe problem is with CircleView itself. Or You change it's frame later in code?
What happens if You change "CircleView" to simply UIView ?
I think that you should move half width to the left, and half height upwards:
-(void)viewDidLoad {
[super viewDidLoad];
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(self.view.center.x-50, self.view.center.y-50, 100, 100];
[self.view addSubView:finalNumberCircle];
}
But I would use something like:
#DEFINE CIRCLE_RADIUS 50
-(void)viewDidLoad {
[super viewDidLoad];
finalNumberCircle = [[CircleView alloc] initWithFrame:CGRectMake(self.view.center.x-CIRCLE_RADIUS, self.view.center.y-CIRCLE_RADIUS, 2*CIRCLE_RADIUS, 2*CIRCLE_RADIUS];
[self.view addSubView:finalNumberCircle];
}
I uploaded a demo https://github.com/luisespinoza/CenterViewTest
I'm wondering how one would go about programming the kind of dropdown tableview that the Vine app uses. If you have never used Vine, I've provided a picture below which depicts the UI Design I'm talking about. Essentially, when you press the left hand UIBarButton, this tableview drops down. When you touch anywhere again, it drops down a little further (5 or 10 pixels) and then leaves the screen with a nice animation.
Just looking for some feedback on how I might go about implementing this. Thanks in advance.
Not sure a UITableView is the way to go about it.
Perhaps you can use REMenu available on Github to get inspired or fork it to customize to your needs.
The REMenu is as close to an exact copy as you can get. I did notice though that it wasn't clipping the top of the menu when it slid down, it slid underneath the status / nav bar which to me didn't look right. Without having looked at the sliding logic (and with my impressive SE reputation of "8"), this is my quick take on how you make the menu appear.
create a view for the contents of the menu (the table view etc)
put it in an enclosing menu collapsed to a zero height, with the content sticking off the top of the collapsed menu view
set the menu view to clip the contents so the top of the menu is not visible, then animate the contents down, as you animate the menu height larger.
This sample uses a simple gradient for the contents of the menu.
#interface BackgroundLayer : NSObject
+(CAGradientLayer*) redBlueGradient;
#end
#implementation BackgroundLayer
+ (CAGradientLayer*) redBlueGradient
{
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors =
#[(id) [UIColor redColor].CGColor, (id) [UIColor blueColor].CGColor];
headerLayer.locations = nil;
return headerLayer;
}
#end
#interface ViewController ()
#property (nonatomic, strong) UIButton* doIt;
#property (nonatomic, strong) UIView* menu;
#property (nonatomic, strong) UIView* nestedView;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// create simple toggle button to test the menu
self.doIt = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.doIt.frame = CGRectMake(50, 50, 50, 44);
[self.doIt setTitle:#"Doit!" forState:UIControlStateNormal];
[self.doIt sizeToFit];
[self.doIt addTarget:self action:#selector(doIt:)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.doIt];
// menu
self.menu = [[UIView alloc] initWithFrame:CGRectMake(20, 200, 280, 0)];
self.menu.layer.borderColor = [UIColor blackColor].CGColor;
self.menu.layer.borderWidth = 3.0;
self.menu.clipsToBounds = YES;
// menu contents
self.nestedView = [[UIView alloc] initWithFrame:CGRectMake(0, -100, 280, 100)];
CAGradientLayer *background = [BackgroundLayer redBlueGradient];
background.frame = self.nestedView.bounds;
[self.nestedView.layer addSublayer:background];
[self.nestedView clipsToBounds];
[self.menu addSubview:self.nestedView];
[self.view addSubview:self.menu];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction) doIt:(id) sender
{
if (!CGRectEqualToRect(self.nestedView.frame, CGRectMake(0, 0, 280, 100)))
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 100);
self.nestedView.frame = CGRectMake(0, 0, 280, 100);
}];
}
else
{
[UIView animateWithDuration:0.15 animations:^{
self.menu.frame = CGRectMake(20, 200, 280, 0);
self.nestedView.frame = CGRectMake(0, -100, 280, 100);
}];
}
}
#end
Cheers.
The Problem with the REMenu is, it creates the viewControllers every time the user taps on particular section, which should not be the case. It should persist the state of each screen attached there.
In my app, I have a split screen in which the detail view is a scrollview. I have 5 tables which are subviews of my scrollview in which 3 table views are side by side on top and 2 table views are side by side on bottom
I have already implemented a way in which when I click any of the rows of any of the table in the scrollview, that view disappears and another view zooms into its position.
I write the following code in the didSelectRowAtIndexPath in the middle table subview,
CGFloat xpos = self.view.frame.origin.x;
CGFloat ypos = self.view.frame.origin.y;
self.view.frame = CGRectMake(xpos+100,ypos+150,5,5);
[UIView beginAnimations:#"Zoom" context:NULL];
[UIView setAnimationDuration:2];
self.view.frame = CGRectMake(xpos,ypos,220,310);
[UIView commitAnimations];
[self.view addSubview:popContents.view];
popContents is the view I need to zoom into to the view previously occupied by that particular table view and that happens correctly.
However the problem that I am facing is that since there is another table subview in the side, if I increase the frame size to say 250 or so, the part of the zoomed in view gets hidden by the tableview on the side ( as its as if a part of the zoomed in view goes under the tableview on the side).
Is there anyway to correct this so that my zoomed in view would not get hidden by the tableviews on its sides?
I hope I have explained my problem correctly...
UPDATE:
Here is the code I am using for adding the subviews for the scrollview
// Scroll view
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 30, 1000, 740)];
scrollView.contentSize = CGSizeMake(1000, 700);
scrollView.delegate = self;
scrollView.scrollEnabled = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
[self.view addSubview:scrollView];
aView = [[aViewController alloc] initWithNibName:#"aViewController" bundle:nil];
aView.view.frame = CGRectMake(10, 25, 220, 310);
[aView loadList:objPatients];
[scrollView addSubview:aView.view];
bView = [[bViewController alloc] initWithNibName:#"bViewController" bundle:nil];
bView.view.frame = CGRectMake(10, 350, 220, 310);
[bView loadList:objPatients];
[scrollView addSubview:bView.view];
cView = [[cViewController alloc] initWithNibName:#"cViewController" bundle:nil];
cView.view.frame = CGRectMake(240, 25, 220, 310);
[cView loadList:objPatients];
[scrollView addSubview:cView.view];
dView = [[dViewController alloc] initWithNibName:#"dViewController" bundle:nil];
enView.view.frame = CGRectMake(240, 350, 220, 310);
[enView loadList:objPatients];
[scrollView addSubview:dView.view];
eView = [[eViewController alloc] initWithNibName:#"eViewController" bundle:nil];
eView.view.frame = CGRectMake(470, 25, 220, 310);
[eView loadList:objPatients];
[scrollView addSubview:eView.view];
say for example, I add the code for didSelectRowAtIndexPath in cViewController subview...
This is a guess since I would need to know how your table views are added to the scroll view, but the middle table view was probably added before the one on the side. Views are "stacked" in the order they're added with the last one on top. You'll need to get the scroll view to move the middle view to the front with this method
- (void)bringSubviewToFront:(UIView *)view
The best way to do that would be to create a protocol for the table views and make the scroll view the delegate. The method would be something like this
- (void) moveAViewToFront: (MyTableView *) aTableView
{
[self.view bringSubviewToFront: aTableView.view];
}
You would then call the delegate method before setting up the animation.
Edited
After a little more thought I realized that the subviews have a reference to their superview so this bit of code should provide an idea on how to solve the problem. I created a test app which has a view controller which adds two sub views. The view controller header file is MoveSubviewViewController.h
#import <UIKit/UIKit.h>
#interface MoveSubviewViewController : UIViewController
{
}
#end
and it's implementation is
#import "MoveSubviewViewController.h"
#import "MoveableSubview.h"
#implementation MoveSubviewViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Create two overlapping subviews. The blue subview will start at the top of
// the frame and extend down two thirds of the frame.
CGRect superviewFrame = self.view.frame;
CGRect view1Frame = CGRectMake( superviewFrame.origin.x, superviewFrame.origin.y,
superviewFrame.size.width, superviewFrame.size.height * 2 / 3);
MoveableSubview *view1 = [[MoveableSubview alloc] initWithFrame: view1Frame];
view1.backgroundColor = [UIColor blueColor];
[self.view addSubview: view1];
[view1 release];
// The green subview will start one third of the way down the frame and
// extend all the to the bottom.
CGRect view2Frame = CGRectMake( superviewFrame.origin.x,
superviewFrame.origin.y + superviewFrame.size.height / 3,
superviewFrame.size.width, superviewFrame.size.height * 2 / 3);
MoveableSubview *view2 = [[MoveableSubview alloc] initWithFrame: view2Frame];
view2.backgroundColor = [UIColor greenColor];
[self.view addSubview: view2];
[view2 release];
}
#end
The subview class is MoveableSubview with another simple header
#import <UIKit/UIKit.h>
#interface MoveableSubview : UIView
{
}
#end
and implementation
#import "MoveableSubview.h"
#implementation MoveableSubview
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Move this view to the front in the superview.
[self.superview bringSubviewToFront: self];
}
#end
The thing to do is to add the
[self.superview bringSubviewToFront: self];
line before setting up the animation.