20150320 2116qtime 1316CST Bloc section 36- Blocstagram- Like Button working here to make a 'like' button for our app. We'll get to tie into the instagram API again for that and make a little graphic and some fun stuff. We'll use Core Animation, a framework by apple for drawing stuff. Should be fun. We've got a packet of images from Bloc to assist with this one, and we just dragged and dropped them into the images.xcassets file. We'll be working with Core Animation here. Core animation renders graphics and creates animations- it's built into iOS and every animation you see uses core animation under the hood. Some basics: Visual content is represented by layers, using the CALayer class and subclasses thereof. Animations are made selecting before and after values for the CALayer properties. So, you rotate the layers, make them opaque, fade them between colors, etc. The animations are CAAnimations and its subclasses. Lets get rolling. We'll start by making a circular spinner. Started with a new UIView subclass. Some properties there. In the .m we're adding a property for a CAShapeLayer. We'll create circleLayer by overriding the getter and creating it the first time it's called. This is known as Lazy Instantiation. Mass of code time. Okay, piece by piece. The first line calculates a CGPoint representing the center of the arc- our arc is an entire circle. Then arcCenter is used to construct a CGRect which our spinning circle will fit inside. Here we make a UIBezierPath object- a bezier path is a path which can have both straight and curved line segments. Ours makes a new bezier path in the shape of an arc with the start and end angles in radians. our smoothedPath is a smooth circular path. Here we crate a CAShapeLayer, a core animation layer made from a bezier path. We set its content scale, just like UIImage's scale, 1.0 on regular screens and 2.0 on retina. Frame is set to rect from earlier. Center of the circle should be transparent so we can see the heart, and the border to be the defined stroke color. lineCap specifies the shape of the ends of the line, and lineJoin specifies the shape of the joints between parts of the line. finally we give the layer the path from our UIBezierPath object. Next we'll make a mask layer and set its size to be the same. Mask layers change the opacity of its layer's content. Our image, angle-mask, looks like this: and that'll allow our circle to have a gradient on it. Now we spin the mask. There's the mask definition. here's the spinning. Completes a spin every second- animationduration is 1. We specify a linear animation as opposed to a geometric which will look like it's easing in and out. We animate the layer's rotation transform from 0 to 2pi. One full circular turn. WELCOME BACK TO GEOMETRY CLASS We repeat an infinite number of times. fillMode specifies what happens when the animation is complete- kCAFillModeForwards in ours leaves the layer on screen after the animation. Then we add animation to the layer. Might be fun to play with autoreverses. Next we animate the line that draws the circle itself. Two CABasicAnimations- start of the stroke, and end. Both are added to CAAnimationGroup which allows multiple animations to be grouped and run concurrently. We add the animations to the circle layer. And we're done building the animation. 2204h. end video 1 2205 start video 2 Creating a method to ensure that the circle animation is positioned properly. Called from a few places in our finished product. There. That just positions the circle in the center of the view. When we add a subview to another view using [UIView -addSubView:] the subview can react to this in [UIView - willMoveToSuperview:], so we'll implement that to ensure our positioning is accurate. Now we'll need to update the position of the layer if the frame changes. if we change the radius of the circle that will affect positioning. We can update that by recreating the circle layer any time the radius changes. We'll inform self.circleLayer if the other two properties, stroke width or color, change. Finally, we'll set some default values in the initializer and provide a hint about our size. That's it! Circular spinning view complete. Now to make a like button to use it. 2218h end of video 2. 2219 start of video 3 new class called LikeButton, a subclass of UIButton. Our header file will define four possible states the button might be in and expose a property for storing this state. In the .m file we'll import the spinner view class, define the image names, and make a property for storing the spinner view. In the initializer we'll create the spinner view and set up the button. Some new properties here- contentEdgeInsets provides a buffer between the edge of the button and the content, concentVerticalAlignment specifies the alignment of the button's content. By default it's centered, but we want it at the top so that the like button isn't misaligned on photos with long captions. We set the default to not liked, as well. Spinner view's frame should be updated whenever the button's frame changes. And now we write code that updates the button based on the set state. There we go. Updates the button's image and userInterctionEnabled property based on LikeState passed in. Also shows or hides the spinner view as appropriate. 2237h. End of video 3. 2238h. start of video 4 Now we're updating the media model to track like state. The button is just a view- shows what it's told. We'll need to pass it the info on whether the user has liked the image. start by importing the like button into media.h so that the likestate, list of possible states, is defined. Adding a property to keep the media item's state. in the .m, figure out whether the user has already liked the image. This goes in initWithDictionary. adding appropriate encode/decode calls… Now media items know whether they've been liked! Lets add the button to the table cell. In the delegate protocol in MediaTableViewCell.h we add a method indicating the button was pressed. In the implementation file (the .m) we import the like button and create a property for it. In the initializer we need to do three things: 1. create the button, 2. add it to the view hierarchy 3. update the layout constraints with its location. In the autolayout constraint we set an explicit width of 38. In setMediaItem we need to display the correct state on the button. When the button is tapped, we inform the delegate. Then we need to update the data source to tell the instagram API when a user likes or unlikes an image. 2256h end video 4 2257h start video 5 Adding a new method called toggleLikeOnMediaItem: to the data source. For the implementation, we need to understand HTTP verbs. Also known as HTTP methods. When we connect to a server, we make a NSURLRequest object. As part of the object we need to specify the action we want the server to take. The most common verb is GET, we've used it in this app a bunch of times. When you GET a URL you are asking for the most recent version of a resource. Responses to GET never create modify or remove data, only transmit it. Some other verbs include HEAD, which requests only the HTTP headers, POST which creates a new resource related to the one at the specified url, PUT which updates the resource at the specified URL or creates it if it's not there, and DELETE which deletes the resource at the specified url. a resource could be anything- a like, a comment, an image, a caption, whatever For our like button, when we like an image we'll create a POST request, and when we unlike an image we'll create a DELETE request. 2317 end video 5 20150321 0526qtime start video 6 Alright, time to code into DataSource for the implementation of the POST request and DELETE to unlike. Okay. Nice block of code. Assume mediaItem.likeState is LikeStateNotLiked (the default), therefore when the user calls this method they're trying to like the image. Set to liking- so the circular animation begins. Make a post request to the url generated at the top of the method, and if it worked update the state to LikeStateLiked. If it didn't revert the state back to LikeStateNotLiked. Either way, reload the row again to update the comment. The delete will be similar but with a DELETE request. And now we need to connect the cell to the Data Source. We do this in ImagesTableViewController.m. Okay! Time for a test run. Hm. Spins a bit and freezes. So, probably a problem with the response to the API request. Yup. Lets open the console. po error in the (lldb) prompt will print the description of the NSError object. The server returned a HTTP 400 error. So. The user has to reauth to grant likeability. The Instagram API authentication docs state that all apps have basic read access by default, and if you plan on asking for extended access such as liking, commenting, or managing friendships, we have to specify these scopes in our authorization request. So we'll need to update the login controller to request these permissions. had to reset content and settings to clear out our data. Sweet! That's enough for tonight. The assignment has a couple of pieces, we'll keep it for tomorrow. 0549h end video 6. 2103h start video 7 The assignment includes 1) playing around with the animation and shape values in CircleSpinnerView. setting autoreverses to YES makes for a weird ghost spinner sometimes. setting the _circlelayer.fillColor = [UIColor to graycolor instead of clear makes a neat radar looking effect. I like it and i'm keeping it. moving on. Part 2 of the assignment is to add a label to mediaTableViewCell that displays the current number of likes. lessee. Made a label, not sure how to set the text. We'll finish the label and its positioning first. Screw it. I'll do that with Steve during our session. Next section is to save the changes to disk when the state successfully changes. Also for steve! Sending it up via github and moving on. I'll see if he has some time to go through this later. Moving on. 2146h End Blocstagram Add Like Button video 7