I added an image button into the interface builder and I want to position it using CGPointMake so I added the button IBOutlet into the .h file and then added a CALayer in the .m file, here is my code...
in .h file
// This is the .h file
#interface ViewController : UIViewController
{
IBOutlet UIButton *thebtn;
}
in .m file
// This is the .m file
- (void)viewDidLoad
{
CALayer *btn = thebtn.layer;
btn.position = CGPointMake(480, 150);
btn.opacity = 0.4f;
[self.view.layer addSublayer:btn];
}
So that is great but my issue that the button position is not changing but the button opacity was changed so how can I fix that issue?
Thanks in Advance
Try to do the following:
First, move your code to viewDidAppear. Get rid of the layer code. Also, add setNeedsLayout call. Here is an example:
-(void)viewDidAppear
{
thebtn.center = CGPointMake(480, 150);
thebtn.opacity = 0.4f;
[self setNeedsLayout];
}
UPDATE
I figured out what the problem was. You have to turn off "Auto-Layout". After that your view will be repositioned to where you want.
Please see this link to see how to disable Auto Layout
Hope this helps!
I think for such purposes as changing position and opacity of a view, you'd better operate with a UIView object (UIButton in your case), instead of its CALAyer property. This should work:
- (void)viewDidLoad
{
thebtn.center = CGPointMake(x, y);
thebtn.alpha = 0.4f;
}
Related
I want to add working UISliders as a subview to the map view of the DJI-iOS-UXSDK-demo (https://github.com/DJI-Mobile-SDK-Tutorials/iOS-UXSDKDemo). By "working" I just mean they should be freely draggable, right now they can only be moved in tiny steps to the right.
I tried adding the sliders using a .xib-View and just adding them programmatically. Both work fine for a generic MKMapView but not for the map view implemented in DJI-iOS-UXSDK-demo. I tried adjusting the frame sizes of the map view, the sliders and the .xib-view, but none of these changed anything for me.
To reproduce, please clone https://github.com/DJI-Mobile-SDK-Tutorials/iOS-UXSDKDemo and install the pods. Then add the following code to viewDidLoad in DefaultLayoutViewController.m after [super viewDidLoad]:
//Get the map view instance and switch it into the main view
MKMapView *mapView;
if ([self.previewViewController respondsToSelector: #selector(mapWidget)]) {
mapView = [[self.previewViewController valueForKey: #"mapWidget"] mapView];
[self performSelector:#selector(previewViewTapped:) withObject:nil];
};
//Add the slider
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(25, 50, 200, 10)];
[mapView addSubview:slider];
You can ignore the registration error, as fixing that didn't change anything for me. The slider appears on the map view as expected, however it can only be dragged in small steps to the right. Adding a listener with addTarget:action:forControlEvents: also worked for me, however it is useless as long as the slider can't be dragged freely.
I followed your reproduction steps, and was able to reproduce and also fix the issue.
I simply added the slider as a subview of the DefaultLayoutViewController.m's view instead of a subview of the MKMapView and it starting to work smoothly.
//Get the map view instance and switch it into the main view
MKMapView *mapView;
if ([self.previewViewController respondsToSelector: #selector(mapWidget)]) {
mapView = [[self.previewViewController valueForKey: #"mapWidget"] mapView];
[self performSelector:#selector(previewViewTapped:) withObject:nil];
};
//Add the slider
UISlider *slider = [[UISlider alloc] initWithFrame:CGRectMake(25, 50, 200, 10)];
[self.view addSubview:slider]; // <------ Add as a subview of the view controller and not the mapView
I worked around it by adding my own UIPanGestureRecognizer to the slider with the target
- (void)sliderMoved:(UIPanGestureRecognizer *)recognizer {
if([recognizer.view isKindOfClass:[UISlider class]]) {
UISlider *slider = recognizer.view;
slider.value = (slider.maximumValue - slider.minimumValue) * [recognizer locationInView:recognizer.view].x / recognizer.view.frame.size.width + slider.minimumValue;
//call the listener or just place your code here
[self sliderChanged:slider];
}
}
Doesn't solve the underlying problem, but this is what works best for me. I would still be interested in finding a fitting delegate for when the views change places if someone reading this happpens to know it.
I am aware that this question has been asked many times, but I have found a partial resolution.
I have a custom class GraphView in which I have several sliders which change the graph parameters and instigate a redraw using [self setNeedsDisplay]. The only way I can get the setNeedsDisplay to work is to have the view of type GraphView just under the View Controller and the slider just under (and inside) the GraphView (in the storyboard hierarchy). This is problematic since the slider must be inside the graph.
Here is an mcve #interface:
#import <UIKit/UIKit.h>
#interface GraphView : UIView
#property float red;
#property __weak IBOutlet UITextField *redout;
#property UIBezierPath *aPath;
#property CGPoint aPoint;
- (void)drawRect:(CGRect)rect;
- (id) initWithFrame:(CGRect)frameRect;
- (IBAction)red_rabi:(id)sender;
Here is the mcve #implementation:
#import "GraphView.h"
#implementation GraphView
- (id) initWithFrame:(CGRect)frameRect
{
if ((self = [super initWithFrame:frameRect]) != nil)
{
_red=1.0;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
int i;
NSString *redstr;
float width, height;
width = rect.size.width;
height = rect.size.height;
_aPath =[UIBezierPath bezierPathWithRect:rect] ;
[_aPath setLineWidth:1.0];
_aPoint.x=0.0;
_aPoint.y=0.0;
[_aPath moveToPoint:_aPoint];
redstr = [NSString localizedStringWithFormat:#"%6.2f", _red];
for (i=1;i<400;i++)
{
_aPoint.x=i*width/400.0;
_aPoint.y=height-height*(sin(i*_red/30.)+1.0)/2.0;
[_aPath addLineToPoint:_aPoint];
}
[_aPath stroke];
}
- (IBAction)red_rabi:(id)sender
{
NSString *redstr;
UISlider *slider = (UISlider *)sender;
_red= slider.value;
redstr = [NSString localizedStringWithFormat:#"%6.2f", _red];
_redout.text = redstr;
[self setNeedsDisplay];
}
#end
If you place a generic View just underneath the View Controller (which I didn't touch), change the generic View's class to GraphView, and place a slider and TextField inside the GraphView (connecting them to the outlet and action), this app will generate a few cycles of a sine wave with the frequency controlled by the slider and its value displayed in the TextField.
If you want the slider and TextField in another view, one must use an enveloping view for all three items (GraphView, slider, text) and one cannot connect the slider and TextField to the GraphView using Ctrl_drag to the GraphView.h file. To remedy this, I placed a generic Object at the highest level and renamed it GraphView - I could then connect the slider and TextField. Although the Textfield reads correctly, the slider doesn't update the GraphView.
By the way, essentially the same code with the GraphView and slider in separate views works perfectly in OS X.
Sorry for the length of this query and thanks!
Problem solved! Due to an interesting SO post from three years ago (about connecting to subviews of UIView), I discovered that one merely drags (not Ctrl_drag!) from the action or outlet circle (in the .h file) to the control and that's it. Works perfectly even when the controls are in a different view from the subclassed UIView. Works equally well with outlets as with actions though you always drag away from the circle.
Thanks to all for their help.
so there must be something simple I am missing because I just can't figure out how to move my UIView
This is what I have so far
my .m file
- (void)viewDidLoad
{
[super viewDidLoad];
[self buttonLayout];
}
-(void)buttonLayout
{
UIView *theView = [self buttonGroup];
theView.center=CGPointMake(50, 50);
}
My .h file
#property (nonatomic, retain) IBOutlet UIView *buttonGroup;
So this is what I have so far and i just cant seem to get it to move at all
P.S i don't want it animated as i'm getting my layout to move to compensate for different iPhone screen sizes :)
I have not tested this, but it seems like you need to:
A) Remove the view from its parent.
B) Adjust the center of its 'frame', not the view.
C) Re-add the subview.
Like so:
UIView *theView = [self buttonGroup];
CGRect theFrame = theView.frame;
[theView removeFromSuperview];
theFrame.origin.x = 50.0;
theFrame.origin.y = 50.0;
theView.frame = theFrame;
[self.view addSubview:theView];
This should work with autolayout.
As the other poster says, you can't move views around by changing their center or frame when you have auto-layout in effect.
Instead, you have to add a vertical and horizontal constraint that positions the view from the top/left edge, connect an outlet to the constraint, and then change the constant value on the constraint from code.
I had similar problem , but when i tried to change the origin as suggested in first solution , i got "Expression not assignable" error.
so i solved it with below solution in viewDidLoad().
First save its center in some temp variable , then change the centre and add again to its parent view.
CGPoint buttonCenter = self.yourButton.center;
[self.yourButton removeFromSuperview];
buttonCenter.y = buttonCenter.y + 50.0;
self.yourButton.center = buttonCenter;
[self.view addSubview:self.yourButton];
Recently I used UIDynamics to animate an image view into place. However, because its autolayout y-pos constraint was set to off-screen, when navigating away from the screen and then returning to it, my image view was being placed off-screen again. The animation took about 3 seconds, so after three seconds I just reset the constraint. That feels a little hacky.
So my question is this: what is the proper way to handle autolayout and UIDynamics at the same time?
This is not really a dynamics problem. Autolayout is incompatible with any view animation, or any manual setting of the frame: when layout comes along, it is the constraints that will be obeyed. It is up to you, if you move a view manually in any way, to update the constraints to match its new position/size/whatever.
Having said that: with UIKit Dynamics, when the animation ends, the animator will pause, and the animator's delegate is notified:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIDynamicAnimatorDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UIDynamicAnimatorDelegate/dynamicAnimatorDidPause:
So that is the moment to update the constraints.
You have a nice solution provided by Geppy Parziale in this tutorial.
Basically you can create an object that conforms to UIDynamicItem:
#interface DynamicHub : NSObject <UIDynamicItem>
#property(nonatomic, readonly) CGRect bounds;
#property(nonatomic, readwrite) CGPoint center;
#property(nonatomic, readwrite) CGAffineTransform transform;
#end
That needs to init the bounds or it will crash:
- (id)init {
self = [super init];
if (self) {
_bounds = CGRectMake(0, 0, 100, 100);
}
return self;
}
And then you use UIDynamics on that object and use the intermediate values to update your constraints:
DynamicHub *dynamicHub = [[DynamicHub alloc] init];
UISnapBehavior *snapBehavior = [[UISnapBehavior alloc] initWithItem:dynamicHub
snapToPoint:CGPointMake(50.0, 150.0)];
[snapBehavior setDamping:.1];
snapBehavior.action = ^{
self.firstConstraint.constant = [dynamicHub center].y;
self.secondConstraint.constant = [dynamicHub center].x;
};
[self.animator addBehavior:snapBehavior];
I have the following which works fine for somethlike a UIImageView. I am not sure if a UIView animation would work with a contained controller? I know I am animating the view's frame but just not sure if this would / should work (the below code is just proof-of-concept). But even if it did, the below is not working. Any ideas on why not?
thx in advance
edit #1
I've added a screen shot that shows a custom view controller in yellow with the phrase holla in it. As you can see the below code aniamtes the frame but not the elements of that view controller. Is this intended? Is there a workaround?
#import "MyViewController.h"
#interface ViewController ()
#property (nonatomic, strong) MyViewController *firstIV;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// lets add a few UIImageViews that we will animate
MyViewController *tmpFirstIV=[[MyViewController alloc] init];
tmpFirstIV.view.backgroundColor=[UIColor yellowColor];
tmpFirstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 100.0f);
self.firstIV=tmpFirstIV;
[self addChildViewController:self.firstIV];
[self.view addSubview:self.firstIV.view];
[self.firstIV didMoveToParentViewController:self.firstIV];
-(void)updateAction:(id) sender{
NSLog(#"Here are some results");
if([self.updateButton.currentTitle isEqualToString:#"update this"]){
[UIView animateWithDuration:6.0f animations:^{ // yeah I know, long animation
self.firstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 0.0f);
}];
[self.updateButton setTitle:#"make bigger" forState:UIControlStateNormal];
}else{
[UIView animateWithDuration:6.0f animations:^{ // equally long
self.firstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 100.0f);
}];
[self.updateButton setTitle:#"update this" forState:UIControlStateNormal];
}
}
edit2
doesn't seem to be doing anything... will look into this layer.
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
CGRect viewBounds = self.view.bounds;
self.myLabel.frame = CGRectMake(100.0f,viewBounds.size.height-20.0f,50.0f,50.0f);
}
You are only animating the frame of the yellow box, not the label. The frame of the label will not change just because its superview's frame has changed. You either need to:
Write code to animate both objects when the button is tapped
Write code to automatically move the label when the box is resized, typically by overriding your view controller's - (void)viewWillLayoutSubviews method.
If you're targeting iOS 6 only, you can use the new auto-layout feature. Ray Wenderlich has an excellent tutorial.