A Curriculum of Guides about Blocks in Objective-C

Recently, there was a question about blocks that I couldn’t answer so I took a note to review it.

I came upon some great articles. Seeing the same elephant described by different people invariably leads to a comprehensive understanding of the elephant (much in the same way that taking beginner swing dance classes from 4-5 people leads to better fundamentals).

With no desire to write an inferior blog post, I’m going to review the ones I found, much the same way that I review restaurants.

The Guides

Mike Nachbaur: Using GCD and Blocks Effectively

Tutsplus: Objective-C Succinctly: Blocks

AppCoda: Introduction to Objective-C Blocks

Apple Working with Blocks

Matt Nunogawa Objective C Blocks: Summary, Syntax & Best Practices

Conrad Stoll Blocks, Operations, and Retain Cycles

I’ll be looking at:

  • explanation of why it’s important
  • completeness vs depth
  • the example code
  • ease of transferring examples to projects
  • how beginner friendly it is

Which should I read first?

Starting from Square One

Go with Tutsplus: Objective-C Succinctly: Blocks. It’s short, has useful examples and memory is well explained with diagrams. It stops short of retain loops though, which is a vital topic. Also lacking is why blocks are used (when would I want to use a block? what would I have used otherwise?).

AppCoda (Introduction to Objective-C Blocks) is really known for their app tutorials. They explain very well why you would use blocks and lead you through an example of how it would be used in a practical situation with an actual app.

Mike Nachbaur: Using GCD and Blocks Effectively extends deeper into asynchronous callbacks, introduces Grand Central Dispatch, and the idea that retain cycles can happen.

For Programmers from Other Languages

Matt Nunogawa (Objective C Blocks: Summary, Syntax & Best Practices) writes his blog directed at programmers from other languages and overviews blocks with greater examples for retain cycles and how to avoid them.

Bonus: Conrad Stoll (Blocks, Operations, and Retain Cycles) writes about a real example of a bug that his team faced with retain cycles and blocks.

For Quick Reference After Learning It Once

Apple Working with Blocks documentation can be a bit dry and doesn’t give much guidance for when to use it, but if that doesn’t bother you, then by all means go ahead. It’s very comprehensive.

Flatiron School Week 12 – Learn Swift or Objective-C?

I was asked whether a person with no iOS experience should learn Swift or Objective-C. After talking to a few people at the school, I’ve come to a simple conclusion.

Some Observations about Swift

In a couple days of playing with Swift, I’ve noticed that I could pick it up pretty easily. The frameworks are the same, but there’s some cool new syntax that will save a lot of typing (closures come to mind). I’ve also found that similar to learning Spanish and French at the same time, learning Swift will confuse your Objective-C syntax.

Learn Objective-C if you plan to be a professional iOS developer within the next year

For professional iOS developers, most companies are still in Objective-C and you will be expected to read and write it. Simple as that.

Learn Swift if you want to just work on your own side projects

Especially if you’re working on iOS development on the side, by the time you’re familiar enough with iOS frameworks, Swift will be more mature, more usable by then.

Flatiron School Week 9 – Resolving my first real git merge conflict

This week we started our client projects. 19 people broke down into teams of 4 or 5. There were four clients and our team had four people. This was the first time that I really needed to deal with git merge conflicts and develop a workflow.

It wasn’t long before I had a conflict.

There were three files that I conflicts with:

  1. AppDelegate.m
  2. the .pbxproj file
  3. a xccheckout file

1. AppDelegate.m

I knew how to resolve the first one, but as I tried to do that, my file directory on the left side of Xcode disappeared. It was time to ask for help. Zach, one of our TAs, helped me put together a plan to resolve the conflicts.

I had to back out of the merge, so I used

git merge --abort

and then reset the head to the last commit because there some uncommit changes that I didn’t want anymore.

git reset --hard <commit id>

2. the .pbxproj file

Apparently that file directory is stored in the .pbxproj file. The way that we resolved this was by telling git to just join the two versions together in the .gitattributes file by adding:

*.pbxproj merge=union

Another way to resolve this is to choose one version of the pbxproj.

git checkout --ours <directory/file>

Replace “ours” with “theirs” to get the version being merged into the current branch.

I had to add in some files that were no longer referenced by the pbxproj file after the merge using this second method.

3. a xccheckout file

For the .xccheckout file, this was a file that should have been ignored. I added this to the .gitignore file.

*.xccheckout

The problem was that both versions that I was trying to merge still had this file in tracked files. I had to remove this file from both repos by running:

git rm --cached <directory/file>

Once the .xccheckout file was removed, I could merge the two repos.

Both the .gitignore and .gitattributes files should be set up at the beginning of a project.

Update

Since that week, we’ve had to resolve conflicts many times. Following Github’s workflow has been pretty useful. I’ve found that the best way to resolve conflicts is prevention. We designed our workflow so that each person worked on a different part of the app and that minimized conflicts.

Flatiron School Week 5 Questions and Answers

Question: Should I be using frame or constraints to draw views in iOS?

Answer: Constraints. Autolayout will be able to adjust to the four different screen sizes, so you don’t have to. It’s like the CSS of iOS.


Question: I know how to animate the background color of a UIView from black to red with animateWithDuration … What do I use when I want to make it go from black to blue to red?

Answer: I could use two animations with different durations, or key frame animations.


Question: One of the labs this week was making a game with four draggable images. After finishing the lab, I noticed that I could drag multiple images at a time and that they would collapse to one location at the end of the drag, because I had only accounted for one object. How can I make it so that the user can only drag one object at a time?

Answer: One solution I found online used one gesture recognizer in the superview and detected which object was pressed using hitTest:. However, this did not solve my problem. I could still take the two. This question is still open, but I can imagine a solution where I store the locations of a four objects in an array and update it as I change each object’s location.

Flatiron School Week 5 Mistakes and Lessons

Topics: Autolayout, views, gesture recognizers

Symptom: The gesture recognizer doesn’t work.

Mistake: Making one recognizer and assigning it to four objects.

Each gesture recognizer can only have one owner and each assignment meant that it was just being reassigned to the latest view.

Lesson: Make one gesture recognizer for each object.


Symptom: Constraints were not working.

Mistake: I had assigned constraints to the view.

Lesson: Constraints are sent to the superview. Gesture recognizers are sent to the view.


Symptom: My game’s win conditions were being satisfied when they shouldn’t have been.

Mistake: When comparing the two views’ centers, I checked if the difference was less than a threshold, when I meant to use the absolute value of the difference.

Lesson: Unexpected wins are bugs too. They should be understood as much as bad bugs.


Symptom: My autolayout had more constraints than I thought I had specified.

Mistake: To deal with the iPhone size plus, I had constraints for when trait collections display scale were 3, but had forgotten to wrap the other constraints in an else statement.

Lesson: Those willTransitionToTraitCollection:withTraitCollection: methods can get really complicated and I have to be careful with the large if statements.


Flatiron School Week 4- Hacking together the TinderClone Swipe Feature

The Tinder Swipe

The central part of Tinder’s design is the swiping mechanism. The feature allows people to focus on one match at a time and make a decision before moving on to the next match.  Good for spending more time on matches and good for reducing the speed of data consumption.

I wanted to learn about animations, so this weekend I hacked together a prototype of the feature.

Demo

All the code can be found here along with a video demo. The code is very dirty, but that’s topic for another week.

Process

Step 1. Look up how to do it.

The next step in my TinderClone project is to make the swipeable images. Richard Kim has a really cool blog entry about how to make this feature three different ways. Since I’m taking on this project to learn to code, I took the “build it from scratch” way and tried to follow Nimrod Gutman’s blog. I totally failed. Twice.

It turns out that I didn’t really understand how UIView and UIImageViews worked. So, I decided to play around with them.

Step 2. Read up on UIViews

I read thought through the Apple View Programming Guide. in particular, the view and window architecture. I didn’t really understand bounds very well, so I started an Xcode project called playingWithBounds, that ended up being my project.

Take-aways:

  • Views are made on top of Core Animation layers (CALayer). Views have animation, but go to CALayer to one layer deeper for more control.
  • Subviews are arranged in an array in the superview.
  • UIContentMode is useful for sizing images. I played around with this later.
  • I thought the bounds property might have something to do with Box Sizing in CSS, but it’s much simpler. The bounds change the frame of the box.

UIView properties

  • frame- used for making objects. CGRectMake()
  • bounds- changes the size of the view, but not the center.
  • center- very important for moving objects around.
  • transform- used to rotate, move and scale objects. It’s the finishing touch. The object is drawn and then transformed. When an object is rotated, it remembers what it’s heigh and width were when it wasn’t rotated.
  • alpha- 1 is opaque and 0 is fully transparent. Great for
  • backgroundColor- useful for seeing where the rectangle is.

Step 3. Playing around with the UIView properties

I made single view project where I drew boxes and changed the bounds, and so on to see what would happen.

Step 4. Playing around with CGAffineTranform

When I got to Coordinate System Transformations, I started playing around with rotating and moving boxes.

I made a slider to rotate an UIImageView.

//slider
        CGRect slideFrame = CGRectMake(100, 400, 200, 20);
        self.pictureRotationAngle = [[UISlider alloc] initWithFrame:slideFrame];
        [self.view addSubview:self.pictureRotationAngle];
        self.pictureRotationAngle.minimumValue = 0;
        self.pictureRotationAngle.maximumValue = 2*M_PI;
        self.pictureRotationAngle.value = M_PI/2.0;
 
        [self.pictureRotationAngle addTarget:self action:@selector(rotateImage) forControlEvents:UIControlEventValueChanged];
 
        self.picture.transform = CGAffineTransformMakeRotation(self.pictureRotationAngle.value);

Step 5. Finding the Event Handling methods

Where other solutions mentioned at the top used UIGestureRecognizers, I imagined that these event handling methods could move my picture around too. They used UITouch objects.

I was touching my UIImageView and nothing was happening. Googling around, I found that UIImageViews are not user interactive by default. So, I had to set it.

    self.picture.userInteractionEnabled = YES;

I also set the

What is UITouch?

I didn’t know, so I built the first three methods and NSLogged the touch and event objects. They were very descriptive.

2014-10-27 18:58:26.919 TinderSwipeFeature[65292:2410541] <UITouch: 0x7fdf015533a0> phase: Moved tap count: 1 window: <UIWindow: 0x7fdf01409040; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x7fdf01403730>; layer = <UIWindowLayer: 0x7fdf01414c50>> view: <UIImageView: 0x7fdf01684780; frame = (37.8671 64.9702; 402.124 402.124); transform = [0.99420459268393058, -0.10750454821160646, 0.10750454821160646, 0.99420459268393058, 0, 0]; clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7fdf01684ac0>> location in window: {256, 332} previous location in window: {255, 332} location in view: {192.38019379786252, 249.92073845470227} previous location in view: {191.38598920517859, 249.81323390649064}

UITouch has a lot of really useful info:

  • Size of the screen: 375 width and 667 points height.
    frame = (0 0; 375 667)
  • The view touched and it’s location
    view: <UIImageView: 0x7fdf01684780; frame = (37.8671 64.9702; 402.124 402.124)
  • Current touch location in view and window
    location in window: {256, 332} 
    location in view: {192.38019379786252, 249.92073845470227}
  • Previous touch location in view and window
    previous location in window: {255, 332}
    previous location in view: {191.38598920517859, 249.81323390649064}
  • Timestamp
    2014-10-27 18:58:26.919

I now had the velocity of touches and I just needed to update the view being touched with a new center value to move it.

That was the key to solving the problem.

I added the other Tinder style animations associated with swiping. All of these were based on using the x location of the touch divided by the width of the phone to calculate a factor that would modify the rotation of the image and the opacity of the overlaps.

Step 6. Playing with contentMode

When I put the picture in the UIImageView, it would not fit in the frame, so I had to set clipsToBounds to YES and change the contentMode from UIViewContentModeScaleToFill to UIViewContentModeScaleAspectFill. UIViewContentModeScaleAspectFit also works but you get white space on the sides.

    self.picture.contentMode = UIViewContentModeScaleAspectFill;
    self.picture.clipsToBounds = YES;

The other options such as UIViewContentModeTop maintain the size of the image, and align the listed edge to the corresponding edge in the box. For example,

  • UIViewContentModeTop aligns the image to the top of the UIImageView.
  • UIViewContentModeTopLeft aligns the image to the top and left of the UIImageView.

Step 7. Animation timing

For the touchesEnded method, I wanted to animate the image moving back into location.

[UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
                ... reset image
            } completion:nil];

CGAffineTransformIdentity removes all transforms (rotations, scaling, translations).

Step 8. Animation for the picture flying off the screen or springing back

To animate the picture flying off to the side, I set a swipe threshold of 30% of the width.

if (differenceInTouchLocationX > aTouch.window.frame.size.width*0.2) {
            [UIView animateWithDuration:0.5 animations:^{
                viewTouched.center = CGPointMake(self.view.frame.size.width*2, currentLocation.y + 0.5 * speedY) ;
 
            }];
            NSLog(@"%@", aTouch);
        }else if (differenceInTouchLocationX < -aTouch.window.frame.size.width*0.3)
        {
            [UIView animateWithDuration:0.5 animations:^{
                viewTouched.center = CGPointMake(-self.view.frame.size.width*2, currentLocation.y + 0.5 * speedY) ;
            }];
        }
        else { 
             ... //springs back to original location
        }

Step 9. Add the overlap images (Like and Nope)

self.like = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 100, 50)];
 [self.like setFont:[UIFont systemFontOfSize:100]];
 self.like.text = @"L I K E";
 self.like.adjustsFontSizeToFitWidth = YES;
 [self.like setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
 [self.like setTextColor:[UIColor colorWithRed:47/255.0f green:156/255.0f blue:28/255.0f alpha:1]];
// self.like.backgroundColor = [UIColor blueColor];
 self.like.transform = CGAffineTransformMakeRotation(-20* M_PI/180);
 self.like.alpha = 0;

Boy, I learned a lot by just reading the documentation and Googling. That was fun!

Flatiron School Week 4- Notes to Self

Blogging is an intentional investment in time

One reason I blog is to review what I’d learned that week.

The other goal in this blog is to document my creative process. There are two styles of documenting process.

  1. Make the thing, write about it afterwards.
  2. Record nuggets of information and write the blog entry along the way.

Both have their benefits and drawbacks.

  • The first gets to the answer faster, but misses out the details of the process.
  • The second slows down the discovery process, but maintains an accurate record of events. Errs on the side of too much detail that people won’t care about.

This weekend, I spent more time coding the Tinder swipe feature and didn’t have as much time to blog. I wanted to finish. I found that trying to recreate my process of discovery after the fact was time consuming and demotivating. Perhaps that’s not a good strategy. If I haven’t been taking notes along the way, I should just provide a summary of key points.

That’s probably what people care about anyway.

Flatiron School Week 4- Questions and Answers

Topics: Cocoapods and Core Data

Question: Should I use [array mutablecopy] or [NSMutableArray arrayFromArray:array]  ?

Answer: Both are the same if the array is already made. Stylistically, prefer the latter.


Question: Is order maintained in Core Data?

Answer: No, if you want to maintain order, add an attribute like createdAt and use an NSSortDescriptor to order by created time.

Flatiron School Week 4 Mistakes and Lessons

Topics: Cocoapods, Core Data

Symptom: The search functionality works most of the time but in a small set of users, the search results are empty.

Mistake: Sending an API call in the prepareForSegue method.

Let’s say that we have a view controller that searches Foursquare for restaurants. If I press submit on my search view controller, I’m pushed to a table view controller with the results. I can put the API call to fetch the data from Foursquare in the prepareForSegue call.

99.5% of the the time, it works, but the other 0.5% of the time, the previous view controller can be removed before our API call has come back with the results and we’ll be left with a table view controller with no results.

This is called a race condition and it’s a very annoying problem to fix because it’s hard to detect and I would have to have millions of users like Facebook to have this problem.

With search view controllers, pass the query to the results view controller and send the API call from there.

Lesson: Avoid race conditions always.


Symptom: I was trying to test a tableViewController to make sure that it worked with Core Data by adding 3 test objects: @”Test”, @”Test Test”, and @”Test Test Test”. I was only seeing one of the three items repeated three times.

Mistake: Messing up basics when moving on to more advanced topics.

I wasted a couple hours last week when I messed up the cellForRowAtIndexRow by writing

cell.textlabel.text = self.store.pirates[[self.tableview indexPathForSelectedRow].row];

instead of

cell.textlabel.text = self.store.pirates[indexPath.row];

Lesson: Watch for simple mistakes, when something is not working as expected.

How do I tap the back button when testing a UITableViewController in KIF?

I tried for the longest time to click the back button, but I couldn’t reference it using an accessibilityLabel. I found that you need the navigation controller to pop the top view controller.

In the it block:

[nav popViewControllerAnimated:YES];

To get the navigation controller add these lines

In the beforeAll block:

UIWindow *window = [UIApplication sharedApplication].keyWindow;
nav = ((UINavigationController*)window.rootViewController);

Since the navigation controller needs to be declared throughout the whole test. Declare a navigation controller

Between the first describe statement and the beforeAll block:

__block UINavigationController *nav;