Why can't access view.center.x / y - ios

I'm just curious why can't you access view.center.x or view.center.y but you can access view.center and change it.
I wanted to write this code:
imgView_.center = scrollView_.center;
if (imgView_.frame.origin.x < 0)
imgView_.center = CGPointMake(imgView_.center.x + (imgView_.frame.size.width - self.view.frame.size.width) / 2, imgView_.center.y);
if (imgView_.frame.origin.y < 0)
imgView_.center = CGPointMake(imgView_.center.x, imgView_.center.y + (imgView_.frame.size.height - self.view.frame.size.height) / 2);
As:
imgView_.center = scrollView_.center;
if (imgView_.frame.origin.x < 0)
imgView_.center.x = imgView_.center.x + (imgView_.frame.size.width - self.view.frame.size.width) / 2;
if (imgView_.frame.origin.y < 0)
imgView_.center.y = imgView_.center.y + (imgView_.frame.size.height - self.view.frame.size.height) / 2;
I find the second way a lot more elegant, but I can't access the x and y, so I thought I'd ask if anyone knows what's Apple's reason for blocking it.

See center property defined in UIView's UIViewGeometry category as below
#property(nonatomic) CGPoint center;
By declaration of center property, we have getter & setter methods for center property. it means we have setter & getter method for structure not for the values inside structure.
and Center is a CGPoint structure variable that has two CGFloat value x and y.
that's by we can't set directly x and y values in center property. it is similar to Frame property.

The UIView property center is a CGPoint which is of type struct and not an ObjectiveC class.
Even though writing this
view.center.x = view.center.x +10;
looks similar to
CGPoint center = view.center;
center.x = center.x +10;
view.center = center;
they are different things.
According to the compiler view.center is a method call [view center] and center.x is accessing the public ivar of the struct CGPoint. If you write all of this in a single line
view.center.x = view.center.x +10;
compiler is unable to resolve this and throws an error Expression is not assignable. Reason is view.center.x is further resolved as function call,
objc_msgSend(view, #selector(center))
Now you are trying to modify the return value of a C function as below
objc_msgSend(view, #selector(center)).x = 20 //(Some value on RHS)
which is not meaningful as the function return value isn't stored anywhere, hence the resulting expression is not assignable.
For more information read through this answer.
Hope that helps!

In the fragment imgView_.center.x the .center is a property access while the .x is a field access. The type of the property is CGPoint, which is a structure type which is passed by value.
If you have a variable of type CGPoint you can directly assign to individual fields, e.g.:
CGPoint myPoint;
myPoint.x = 3;
However when passing a CGPoint value to a function or method you cannot pass just one of it's fields - it makes no sense. E.g. if you have the function:
void doSomething(CGPoint aPoint);
you could pass myPoint:
doSomething(myPoint);
but there is no way to say "just pass the x field" - what would that even mean? The function is expecting a CGPoint, what would passing it one coordinate mean?
When you put the fragment imgView_.center on the left hand side of an assignment you are just using a shorthand for calling a method. E.g.:
imgView_.center = myPoint; <=> [imgView_ setCenter:myPoint];
and just like with the function above there is no way to say "just pass the x field".
The key point is a property access is not a variable access, even though it is often thought of as one, but a function/method call; so you can't assign to the individual fields of a property of structure type.
On the right hand side accessing a field of a property is fine. E.g. on the rhs imgView_.center translates to [imgView_ center] - which returns a CGPoint value, and you can access a field of structure value, so on the rhs imgView_.center.x translates to [imgView_ center].x and all is OK. But note that in both cases (setCenter: and center methods) the methods themselves take/return a complete CGPoint.
HTH.

Related

SCNode doesn't have boundingBox property in Objective-c?

I'm trying to correctly position another SCNode and all the examples I find are using the boundingBox property like so:
let vaseHeight = vaseNode.boundingBox.max.y - vaseNode.boundingBox.min.y
or
glassesNode.position.z = faceNode.boundingBox.max.z * 3 / 4
The problem is that doesn't exist with Objective-c and I can't find anything to replace it?
I was able to find this method
- (BOOL)getBoundingBoxMin:(SCNVector3 *)min
max:(SCNVector3 *)max;
but it returns a bool and how I can know the min and max, that's what I'm trying to find?
The Swift property is returning a tuple. The Objective-C method has two out-parameters.
getBoundingBoxMin:max: is the correct method. The two parameters are populated as a result of the call. The BOOL return value indicates whether the node has a non-zero volume or not.
SCNVector3 min;
SCNVector3 max;
CGFloat vaseHeight;
if ([vaseNode getBoundingBoxMin:&min max:&max]) {
// Process min and max here
vaseHeight = max.y - min.y;
} else {
// Node has a zero volume
vaseHeight = 0;
}

Why does the compiler claim CGRect has no width member?

Note that I'm not trying to set the value in a CGRect. I'm mystified as to why the compiler is issuing this claim:
let widthFactor = 0.8
let oldWidth = wholeFrameView.frame.width
let newWidth = wholeFrameView.frame.width * widthFactor // Value of type '(CGRect) -> CGRect' has no member 'width'
let newWidth2 = wholeFrameView.frame.width * 0.8 // This is fine.
Width is a CGFloat where your multiplier is a Double. Explicitly declare the type of your multiplier:
let widthFactor: CGFloat = 0.8
All the dimensions of a CGRect are of type CGFloat, not Double, and because Swift is especially strict about types, you can't multiply a CGFloat by a Double.
The interesting thing though, is that both CGFloat and Double implement ExpressibleByFloatLiteral. This means that 0.8, a "float literal", can be interpreted as either a Double or a CGFloat. Without context, it's always a Double, because of how the compiler is designed. Note that this only applies to float literals like 3.14, 3e8 etc, and not to identifiers of variables.
So the expression wholeFrameView.frame.width * 0.8 is valid because the compiler sees that width is a CGFloat, so it treats 0.8 as a CGFloat as well. No problems.
On the other hand, when you declare the variable widthFactor, it is automatically given the type Double, because there aren't any more context on that line to suggest to the compiler that you want it to be any other type.
This can be fixed by directly telling the compiler that you want widthFactor to be a CGFloat:
let widthFactor: CGFloat = 0.8
Because, as others have noted, you can't multiply a Double and a CGFloat, the compiler doesn't know what you're intending.
So, instead of giving you an error about the frame property, which you currently think it's doing, it's actually making its best guess*, and giving you an error related to the frame method. No method method has a width property, so what it tells you is true.
*Of course, its best guess is not good enough, hence a human being coming here to ask a question about it. So please file a bug!
Stepping onto my soapbox: This confusion would be avoided if Apple hadn't named the method the thing it returns. The convention to prefix all such methods with get solves the problem. Some convention is important in any language with first-class functions, to disambiguate between properties and methods.
wholeFrameView.frame has no member width. Also, you need widthFactor to be of type CGFloat. Try:
let newWidth = wholeFrameView.frame.size.width * CGFloat(widthFactor)

view.bounds.size.height -vs- view.bounds.height -- Any Difference?

I've noticed that in IOS X-Code using (Swift 4.0), I can ask for the height of a view, V, in at least these two ways:
V.bounds.size.height
and...
V.bounds.height
Is there any actual difference between these two?
I did the option-click thing (which give different definitions, but don't explain any practical difference or reason for one over the other)... and stackoverflow... but here on stackoverflow, all the results are discussing the difference between bounds and frame... which is NOT what I'm asking.
V.bounds.height is only a GET Property. You Can't set a value for this property.
Example:
self.view.bounds.height = 5
This error message results...
Cannot assign to property: 'height' is a get-only property
If you want to assign a value to this property, then you can write...
self.view.bounds.size.height = 5
So you can set value to this object. Have a look at here.
There is small difference. view.bounds.height is a shortcut. You cannot edit it :
view.bounds.height = 150 won't work, but view.bounds.size.height = 150 will.
Actually V.bounds.size.height, height have both get-set property and where as in V.bounds.height, height is only getter property and it always return you height of the rectangle.
For the getter perspective both are same.
In addition to the fact that view.bounds.height is readonly, there is another difference: if you have negative width/height, view.bounds.height will return you the normalized value (the positive one), while view.bounds.size.height will return the real value. These getters are the equivalent of the CGRectGetWidth() CGRectGetHeight() from Obj-C. All these getters from CGRect struct (widht, height, minX, minY...) are returning the normalized values of the CGRect's dimensions and they are recommended in case you want to use them in frame computations.

CorePlot allow user drag rotate PieChart

(As for I got the solution now, it is being shared at the bottom)
Fact is I have been struggling a while about this and I believe quite a lot of discussions I found are related to older versions of CorePlot or unanswered.
Firstly, I am using CorePlot 1.5.1.
I am able to plot a PieChart already and now I would like the user to be able to rotate it by dragging on the screen ( doesn't really matter touch directly the pieChart or the host View).
Using these delegates at the moment:
#interface MyChartViewController : UIViewController<CPTPieChartDataSource,CPTPlotSpaceDelegate,CPTPieChartDelegate>
Got a hostView,
#property (strong, nonatomic) IBOutlet CPTGraphHostingView *hostView;
Made a graph, set as, self.hostView.hostedGraph = graph
and made a PieChart, put into the graph, [graph addPlot:self.mainPieChart];
(I set the pieChart with a strong property to let me refer it anytime)
So, here is my first attempt, and fact is, it is responding, (though not in a desirable way)
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *) self.hostView.hostedGraph.defaultPlotSpace;
[plotSpace setDelegate:self];
(only works by setting plotSpace delegate to self, not sure why, i guess it's about finding a way to receive user's interaction, anyway, then I overwrite these two functions)
Using this value:
static float deltaAngle;
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDownEvent:(UIEvent *)event atPoint:(CGPoint)point
{
float dx = point.x - self.mainPieChart.centerAnchor.x;
float dy = point.y - self.mainPieChart.centerAnchor.y;
deltaAngle = atan2(dy,dx);
return YES;
}
This, in order to save the first touching point
Then sense the dragging do use the difference to make the rotation
( at least I wanted so )
-(BOOL)plotSpace:(CPTPlotSpace *)space shouldHandlePointingDeviceDraggedEvent:(UIEvent *)event atPoint:(CGPoint)point
{
int x = self.mainPieChart.centerAnchor.x;
int y = self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = -angleDifference;
return YES;
}
And here is an image about it, though i think I covered most of the details already.
http://postimg.org/image/bey0fosqj/
It is in landscape mode though.
Fact is I think this would be the most appropriate function to call, but somehow I cannot call it out (pretty sure I set self.mainPieChart delegate/ datasource to self already)
-(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{
(after further testing)
Interesting, after trying to print out different values, by the shouldHandlePointingDevice function (simply clicking), I think i got some ideas now.
the self.mainPieChart.centerAnchor.x / y values always return 0.5 (both)
However, point x, point y are returning values vary from 1-500+,
it seems more like I am comparing two things, though they are on top of each other, from different perspective.
Likely the PlotSpace set delegate part messed that up.
============================================================
So, as for now I still don't know how to call -(BOOL)pointingDeviceDraggedEvent:(id)event atPoint:(CGPoint)interactionPoint{, I tried to put it into a if loop like
if([self.mainPieChart pointingDeviceDownEvent:event atPoint:self.mainPieChart.centerAnchor] == YES)
under my touched function but nothing happened, never mind.
Back to the point, my current solution works well now, even after applying padding.
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
float y = self.hostView.bounds.size.height * self.mainPieChart.centerAnchor.y;
float dx = point.x - x;
float dy = point.y - y;
double a = atan2(dx,dy);
these lines are all same for both press / drag functions, as for drag function,
float angleDifference = deltaAngle - a;
self.mainPieChart.startAngle = angleDifference;
are added before the end
However, the case is slightly different when the Pie Chart is not at the middle, or, in other words, the graph holding the Pie Chart is padded.
( my example somehow is mid centre just to make it easy)
you simply have to mortify the x y float value above, it's easier than I expected.
For example if I have,
graph.paddingLeft = -300.0f;
the value of float x in both press/drag will become
float x = (self.hostView.bounds.size.width + self.hostView.hostedGraph.paddingLeft)*self.mainPieChart.centerAnchor.x;
The pie chart centerAnchor is given as fractions of the width and height. Be sure to multiply the anchor values by the corresponding dimension of the graph before computing dx and dy.

Cant check if CGPoint is not equal CGPointZero

I have a CGPoint declared in a UIView class, in my viewController I try to check if that CGPoint is not equal to CGPointZero, but I get this error:Invalid operands to binary expression ('CGPoint' (aka 'struct CGPoint') and 'CGPoint')
This is the if-statement:
if (joystick.velocity != CGPointZero)
The error points to the != and I dont know why it gives me an error.
joystick is the UIView class, CGPoint velocity is declared like this:
#property(nonatomic) CGPoint velocity;
Try this:
if (!CGPointEqualToPoint(joystick.velocity, CGPointZero))
Explanation: A CGPoint is actually a struct. The binary operand ("==" or "!=") it's only used to compare primitive values, usually useful to compare pointers, which in fact are integers representing a memory position.
As you have a struct, and not a reference to something, you would have to compare each value inside your struct, but fortunately apple already implemented a macro that performs this for you in the case of CGPoint.
If you are curious, you can command-click the macro above and see the implementation:
__CGPointEqualToPoint(CGPoint point1, CGPoint point2)
{
return point1.x == point2.x && point1.y == point2.y;
}

Resources