Why is (self.window) in the following code a condition?
-(void)beginRefreshing {
[UIView animateWithDuration:MJRefreshFastAnimationDuration
animations:^{
self.alpha = 1.0;
}];
self.pullingPercent = 1.0;
if (self.window) {
self.state = MJRefreshStateRefreshing;
} else {
self.state = MJRefreshStateWillRefresh;
[self setNeedsDisplay];
}
}
In this case:
if (self.window) {
which is a short version of
if (self.window != nil) {
is a test whether the view (UIView instance) is in the view hierarchy of any window, that is, whether the view is displayed on the screen.
Certain variables are boolean meaning they can either be equal to true or false. Others can have a wide range of values. for example off you want to do something if an Integer is equal to 5 you can write:
if (myInt == 5) {
//do something
}
If your variable is a Boolean there are only two options so we can write is shorter:
if (myBoolean) {
//case where myBoolean = true
} else {
//case where myBoolean = false
}
In your particular example, self.window evaluates to a Boolean in the following manner: either self.windows exists in the view hierarchy or not (self.windows == true) or it do not (self.windows == false).
I've decided to expand my comment to an answer for future reference.
It's actually a side effect, AFAIK. Because false is defined as 0, and true is defined as anything different than 0. A valid objects address is non-zero so it is treated as true, whereas, thanks to ARC, nil == 0. You can check this by printing this NSLog(#"%p", nil). Bear in mind, that for integers, this means that a negative value, also evaluates to true
It is also worth noting, that because the underlying storage for a boolean is rarely one-bit it may not always be true that when someBool == true, then someBool == 1. This means that taking such a shortcut :
- (NSInteger)increment:(NSInteger)toIncrement ifTrue:(BOOL)condition {
return toIncrement + condition;
}
may not always lead to toIncrement being incremented by 1. The code is valid, becasue a BOOL will get promoted to integer automatically.
This is of course somwhat of an edge case, but still possible.
Related
I am having a heck of a time getting around some very annoying behavior in MapKit. Specifically, I want to show a popover annotation view after the map has animated to a certain visible region.
I can use mapView:regionDidChangeAnimated: to get a callback after the map transition (which I will call the "zoom"), but this only fires if the visible region actually changes.
You see the zoom happens in my app when an annotation (or its counterpart in a table view) is selected: I want to zoom the map to essentially focus on the selected annotation.
The problem is that if the map is already in the correct region, I will get no regionDidChange, and therefore no signal to show my popover view.
I am using variants of setVisibleMapRect: to actually perform the zoom. So I thought I was being smart by comparing the new and old MKMapRects to see if they are equal, and if so manually calling my callback.
The problem is it doesn't work! Even if I determine that the two MKMapRects are not equal, by means of MKMapRectEqualToRect, MapKit just sometimes decides it won't fire the regionDidChange event! Perhaps it has to be over a certain delta or something, I don't know.
So my question is: what the heck is the expected best practice for getting a guaranteed completion from setVisibleMapRect:?
Update: I'm now thinking this might have to do with the edgePadding argument which I am also using:
- (BOOL)vivoSetVisibleMapRect:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets animated:(BOOL)animate {
BOOL equal = MKMapRectEqualToRect(self.visibleMapRect, mapRect);
NSLog(#"Map rects are: %#", equal ? #"equal" : #"NOT equal");
NSLog(#"%#\n\n%#", MKStringFromMapRect(self.visibleMapRect), MKStringFromMapRect(newRect));
[self setVisibleMapRect:mapRect edgePadding:insets animated:animate];
return !equal;
}
As you can see, the edge padding is not taken into account in the comparison, yet it is most likely having an effect on the finally computed mapRect.
Does anyone know how I can perform this test to properly take into account edgePadding?
Update 2: It's now looking like MKMapRectEqualToRect is wrong and therefore completely useless. Check out this log statement:
2016-03-16 17:06:30.841 Mobile[70089:6240786] Map rects are: NOT equal
{{42403042.3, 91858289.9}, {14878.4, 12832.6}}
{{42403042.3, 91858289.9}, {14878.4, 12832.6}}
They look pretty darn equal to me!! 😒
MapRects are defined using Doubles, and comparing Doubles can give unexpected behavior. My recommendation is to define your own comparison that compares to your desired tolerance. For instance, compare to the nearest integer value.
In Swift:
public extension Double {
func roundToInt() -> Int {
let value = Int(self)
return self - Double(value) < 0.5 ? value : value + 1
}
}
public func == (lhs: MKMapRect, rhs: MKMapRect) -> Bool {
if lhs.origin.x.roundToInt() != rhs.origin.x.roundToInt() {return false}
if lhs.origin.y.roundToInt() != rhs.origin.y.roundToInt() {return false}
if lhs.size.width.roundToInt() != rhs.size.width.roundToInt() {return false}
if lhs.size.height.roundToInt() != rhs.size.height.roundToInt() {return false}
return true
}
You can then do a comparison by typing
if mapRect1 == mapRect 2 {
...
}
It turns out MKMapRectEqualToRect is completely broken (at least on the simulator). Don't use it!!
I changed it to instead do a comparison on the strings returned by MKStringFromMapRect, as well as adjusting the map rect with mapRectThatFits:edgePadding: before the comparison and it now appears to be working correctly:
- (BOOL)vivoSetVisibleMapRect:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets animated:(BOOL)animate {
MKMapRect newRect = [self mapRectThatFits:mapRect edgePadding:insets];
BOOL equal = [MKStringFromMapRect(self.visibleMapRect) isEqualToString:MKStringFromMapRect(newRect)];
[self setVisibleMapRect:newRect animated:animate];
return !equal;
}
I am supposed to refactor this line of Objective-C code:
if (_currentImageFrame && ![_currentImageFrame.isSaveToLibrary boolValue]) {}
I think of 2 ways to achieve this, but I am not sure which one is better:
1)
if (self.currentImageFrame != nil) &&
(self.currentImageFrame?.isSaveToLibrary == false)
{
}
2)
if let frame = self.currentImageFrame {
if self.currentImageFrame?.isSaveToLibrary == true {
}
}
Please let me know which is more recommended/correct way to achieve it.
Thanks in advance!
I would use the following:
if self.currentImageFrame?.isSaveToLibrary == true {
// Stuff
}
Your var currentImageFrame is ? so you don't have to check if it's nil, in case of nil, it is not going to check the if condition, and if it's not nil it is going to check the if and if the condition is satisfied it will enter. Furthermore you don't need to use parenthesis in Swift :)
Since self.currentImageFrame is an optional, the more compact way of doing what you ask is taking advantage of the way optional chaining works:
if self.currentImageFrame?.isSaveToLibrary == false
{
// Code
}
If self.currentImageFrame is not nil, self.currentImageFrame?.isSaveToLibrary will return the value of isSaveToLibrary.
If self.currentImageFrame is nil, self.currentImageFrame?.isSaveToLibrary will return nil immediately, without attempting to execute anything that's after the ? (no matter how deep the chain goes).
nil does not equal false (unlike Objective-C, in Swift nil only equals nil and nothing else), hence nil == false evaluates to false, so the code behaves as you would expect.
Note that your 1) option would work but it's completely redundant, as the first check is not needed at all.
I think that your 2) option has a typo, and you intended to write false where you typed true. With the typo fixed it would work as well (since you are actually using optional chaining once again inside the outer condition), but it's using optional binding in a wrong way, since you don't use the the bound constant frame inside the outer conditional body. A more correct (although redundant as well) way of using optional binding would be.
if let frame = self.currentImageFrame
{
if frame.isSaveToLibrary == false
{
// Code
}
}
In this case you don't use optional chaining at all. (Although I suspect that the optimized compiled code would be the same in both cases).
Finally, I suggest you rename isSaveToLibrary as it's grammatically incorrect and its meaning is unclear. I would name it wasSavedToLibrary or shouldSaveToLibrary, depending on the intended use.
I have the following code to determine different action depending upon the type of the rvc:
let rvc = UIApplication.sharedApplication().delegate.window?.rootViewController
if rvc?.isKindOfClass(UINavigationController)
{
push the view controller
}
else
{
present the view controller
}
However when I execute this with a storyboard where the initial view controller is not a UINavigationController, just a descendant of UIViewController then the first branch of the if statement is executing.
UPDATE: The problem actually is with the syntax because in the following code each if statement will evaluate to true:
if rvc?.isKindOfClass(UIViewController)
{
println("UIViewController")
}
if rvc?.isKindOfClass(UINavigationController)
{
println("nav controller")
}
if rvc?.isKindOfClass(NSNotification)
{
println("should not get here")
}
if rvc?.isKindOfClass(put whatever you want here)
{
println("should not get here")
}
Therefore there must be a problem with the syntax/semantic of rvc?.isKindOfClass but what is incorrect with that?
As an addendum to Grimxn's answer (and in response to the question of the difference between if rvc?.isKindOfClass(xxxAnyClassxx) { and if rvc!.isKindOfClass(...) {):
When you use ?.method (i.e., option chaining), the result of that method is encapsulated in a Optional. That's the whole point; the final result of an option chain will either be .Some(the result of the last method in the chain) or nil if any point in the chain resulted in nil.
Testing the logical value of an Optional in an if statement will return true if the Optional contains something (i.e., its value is .Some(...)), and false if it's nil -- it does NOT look at what the value inside the Optional is. In this case, it's that contained value that you actually want to check.
The syntax seems valid. This works in Playground:
class MyNavC: UINavigationController {
}
let v = MyNavC(navigationBarClass: nil, toolbarClass: nil)
let v1 = UIViewController(nibName: nil, bundle: nil)
let b = v.isKindOfClass(UINavigationController) // true
let b1 = v1.isKindOfClass(UINavigationController) // false
...but those aren't optionals (nor are allowed to be).
Trying your code in an actual app with the optionals necessary for your assignment:
let rvc = UIApplication.sharedApplication().delegate.window?.rootViewController
you are correct,
if rvc?.isKindOfClass(xxxAnyClassxx) {
always evaluates true. However, using
if rvc!.isKindOfClass(...) {
gives the correct result.
I am trying to set a switch to turn off another switch when it is on. I know you use a if statement to do this, but I find it to be very different than an HTML if statement. I know my code is not right but it will explain what I need.
- (IBAction)dashieScheme:(id)sender {
if(dashieScheme.state = true) {
(twilightScheme.state = false);
}
}
- (IBAction)twilightScheme:(id)sender {
if(twilightScheme:(id)sender ) {
(dashieScheme.state = false);
}
}
You want to use the following:
[dashieScheme setOn:NO animated:YES];
Also, your if statement needs some work. You want to use == to check for equality. a single = sign is for assignment.
Can someone please educate me why the following does not work? The button never gets set to selected.
[self.boldButton setSelected:isBold];
If I replace the above with an if else statement it works fine. I can also change the setSelected values to 1 or 0, instead of YES or NO and it still works fine.
if (isBold)
{
[self.boldButton setSelected:YES];
}
else
{
[self.boldButton setSelected:NO];
}
So I have a working project, but I don't understand why these two implementations don't deliver the same results. Thanks.
FWIW - I test for bold with another method. Though if the test were flawed, I don't see how the second approach could work, while the first still doesn't.
- (BOOL)isBold
{
CTFontRef fontRef = (CTFontRef)CFBridgingRetain(self);
CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(fontRef);
return (symbolicTraits & kCTFontTraitBold);
}
BOOL is defined like this in <objc/objc.h>:
typedef signed char BOOL;
That means a BOOL can actually hold any value in the range -128 through 127 (inclusive).
-[UIControl setSelected:] works roughly like this:
#define kSelectedBitPosition 10
#define kSelectedBit (1 << kSelectedBitPosition)
- (void)setSelected:(BOOL)selected {
if (((self->_controlFlags >> kSelectedBitPosition) & 1) == selected) {
return;
} else {
self->_controlFlags = (self->_controlFlags & ~kSelectedBit)
| ((selected & 1) << kSelectedBitPosition);
[self setNeedsDisplay];
}
}
(I disassembled the simulator version of UIKit with Hopper to figure that out.)
So, notice two things:
The if statement condition can only be true if selected == 0 or selected == 1. It will never be true if selected has any other value.
The assignment statement (that updates _controlFlags) only uses bit 0 (the 1's bit) of selected. So, for example, if selected == -2, which is logically true in C and has every bit set except bit 0, the assignment statement will still not turn on the bit in _controlFlags.
This means that you must pass 0 or 1 to -[UIControl setSelected:]. No other value will work reliably.
The shortest way to convert all non-zero values to 1 in C is by applying the ! operator twice:
[self.boldButton setSelected:!!isBold];
However, it would probably be better to fix your -isBold method to return a “safe” BOOL instead:
- (BOOL)isBold {
CTFontRef fontRef = (CTFontRef)CFBridgingRetain(self);
CTFontSymbolicTraits symbolicTraits = CTFontGetSymbolicTraits(fontRef);
return !!(symbolicTraits & kCTFontTraitBold);
}