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
Related
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];
}
I'm trying to add an imageView in the ScrollView but I always get the ScrollView empty. Please where would be my issue? While I have added the delegate method <UIScrollViewDelegate> also made the connection in the storyboard.
My code:
- (void) viewDidLoad {
[super viewDidLoad];
[self slider];
}
-(void) slider {
_scrlView = [[UIScrollView alloc]init];
_scrlView.backgroundColor = [UIColor blackColor];
_scrlView.pagingEnabled = YES;
_scrlView.delegate = self;
_scrlView.showsHorizontalScrollIndicator = false;
_scrlView.layer.cornerRadius = 2;
_scrlView.contentSize = CGSizeMake(_scrlView.frame.size.width * 3, _scrlView.frame.size.height);
_img1 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*0),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
_img2 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*1),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
_img3 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*2),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
_img1 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"1234.jpg"]];
_img2 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"1234.jpg"]];
_img3 = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"1234.jpg"]];
[_scrlView addSubview:_img1];
[_scrlView addSubview:_img2];
[_scrlView addSubview:_img3];
}
First, you don't add the scrollView to any view, so it won't be presented on screen.
Second, this:
_scrlView = [[UIScrollView alloc]init];
creates a scrollView with CGRectZero which is the same like writing:
_scrlView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,0,0)];
This means the contentSize is also CGRectZero.
Edit: If you don't want to override the creation of the scrollView (because you have already set it in Interface Builder), remove the line _scrlView = [[UIScrollView alloc]init];.
But if you are using Auto Layout in the storyboard you should call [self slider] in viewDidLayoutSubviews.
You need to set frame first, for getting its width and height,
- (void)viewDidLoad {
[super viewDidLoad];
[self slider];
}
-(void)slider{
_scrlView = [[UIScrollView alloc] initWithFrame:CGRectMake(YOUR_X, YOUR_X, YOUR_WIDTH, YOUR_HIEGHT)];
// do this if you have not added Scrollview in storyboard, if Added, then no need to invoke, init by line [[UIScrollView alloc] init] ;
...
...
[self.view addSubview:_scrlView];
}
I would suggest one more thing, to call your slider method from viewDidAppear method of UIView
Enjoy Coding !!
You need to allocate the ScrollView if have not used a storyboard. Also in that case, specify a frame:
_scrlView = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height)];
You are reallocating the imageviews, try to set image after allocating it once.
Another thing : Dont init scrollview again.
- (void)viewDidLoad {
[super viewDidLoad];
[self slider];
}
-(void)slider{
_scrlView.backgroundColor = [UIColor blackColor];
_scrlView.pagingEnabled = YES;
_scrlView.delegate = self;
_scrlView.showsHorizontalScrollIndicator = false;
_scrlView.layer.cornerRadius = 2;
_scrlView.contentSize = CGSizeMake(_scrlView.frame.size.width * 3, _scrlView.frame.size.height);
_img1 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*0),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
_img2 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*1),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
_img3 = [[UIImageView alloc] initWithFrame:CGRectMake((_scrlView.frame.size.width*2),0,_scrlView.frame.size.width,_scrlView.frame.size.height)];
[_img1 setImage:[UIImage imageNamed:#"1234.jpg"]];
[_img2 setImage:[UIImage imageNamed:#"1234.jpg"]];
[_img3 setImage:[UIImage imageNamed:#"1234.jpg"]];
[_scrlView addSubview:_img1];
[_scrlView addSubview:_img2];
[_scrlView addSubview:_img3];
}
My reply would be
if you are not using Storyboard or Interface builder , also autolayout is unchecked, set the frame of uiscrollview after its allocation and use alloc init only one time for one image. Add image on scrollview and scrollview on superview
if you are not using Storyboard or Interface builder and autolayout is checked, then put proper constraints on scrollview and imageviews. Read this
https://developer.apple.com/library/ios/technotes/tn2154/_index.html
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 have a UIView which is located offscreen and I'm animating the frame so that the view slides in offscreen from the bottom and is visible. I'd like to simultaneously animate the alpha property of a UILabel on the view as well so it fades in. Unfortunately it appears I can't do the alpha animation because the view is offscreen and doesn't appear to take hold. It looks something like this:
nextCell.titleLabel.alpha = 0;
[UIView animateWithDuration:collapsedAnimationDuration animations:^{
CGRect newFrame = lastCell.frame;
newFrame.origin = CGPointMake(lastCell.frame.origin.x , lastCell.frame.origin.y + THREAD_CELL_HEIGHT);
nextCell.frame = newFrame;
nextCell.titleLabel.alpha = 1;
}];
Is it not possible to start animating the alpha of the subview because it's offscreen? If I position the view on screen and then try the animation it looks great but that's not the effect I'm going for. Thanks for your help.
Is this code executed in cellForRowAtIndexPath? If so, try moving it to tableView:willDisplayCell:forRowAtIndexPath:. The table view resets various properties of the cell before displaying it.
From the AppDelegate didFinishLaunching method:
self.myView = [[MyView alloc] initWithFrame:CGRectMake(320, 480, 400, 400)];
self.myView.titleLabel.text = #"test text";
self.myView.titleLabel.alpha = 0;
[UIView animateWithDuration:10.0 animations:^{
CGRect newFrame = self.myView.frame;
newFrame.origin = CGPointMake(0 , 0);
self.myView.frame = newFrame;
self.myView.titleLabel.alpha = 1;
}];
[self.viewController.view addSubview:self.myView];
MyView is just this:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[self addSubview:self.titleLabel];
}
return self;
}
- (UILabel *)titleLabel
{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
}
return _titleLabel;
}
I did no important changes to the code you presented and it worked fine. So assuming you're not doing what Tim mentioned (it won't work if you're doing it), we need more details to help you out.
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.