Christmas time is finished. New Year’s Eve is finished as well. I hope that everyone had a great time and you are ready to start working hard and learning new things! I want to start this year with a wonderful article-tutorial on how to create similar to fast rewind control. Periscope Requirements Xcode 7+ 8+ iOS At least basic Swift knowledge Passion to iOS and development High-level explanation To build this component we will have to use video player — . We will add to begin rewind and calculate finger translation — it will help us to change rewind speed and calculate time. While rewinding is in progress we will use to blur video, and to generate preview thumbnails. AVPlayer UILongPressGestureRecognizer UIVisualEffectView AVAssetImageGenerator OK. Let’s stop talking, . just do it Create single view application and create new class — subclass of UIViewController. Import and put this code inside class declaration: VideoViewController AVFoundation Everything we did here is quite easy: Declared all variables related to AVPlayer which we are going to use in the future; Created custom initializer with videoURL where we set URL and initialize all — AVURLAsset, AVPlayerItem, AVPlayer and AVPlayerLayer; In we add player to view sublayers; loadView In we simply start playing video; viewDidLoad In we return — it hides status bar if our status bar is based on view controller (it can be changed in file). prefersStatusBarHidden true Info.plist Simply set frame of playerLayer to view.bounds — we support both portrait and landscape orientations. OK. Our video view controller is ready for early stage test. In order to test it we have to do two simple steps: , extract and add folder to your project ; Download this ZIP file Resources (do not forget to select required targets) Open file and put this code inside: ViewController.swift Now run project on your device or simulator and make sure that video view controller is presented, video is playing, and all orientations are supported. Our next stage is to recognize touches and blur content. Declare these two variables in : VideoViewController Paste this code to the end of method: loadView Add this code to the end of method: viewWillLayoutSubviews Implement this function: The purpose of this function is to pause video and fade in visual effect view when touches began, and resume video and fade out visual effect view when touches cancelled, ended or failed. In the future we will add more logic to this method. Run project and test long press. That’s how it works on my device: Next step is to create timeline view and implement rewind functionality. But let’s have a very quick look at UI to understand what exactly our timeline should have and how it should work. Periscope As you can see — timeline goes from the left side to the right side of the view controller. It has a white dot which indicates where video was stopped when user started rewinding. If you compare screenshots you will notice, that interval width is different — it is because rewind speed (we will call it in our project) values are not equal. And the last thing to notice is that current time is always in the middle of the timeline. zoom Create as a subclass of UIView and declare these variables: TimelineView The purpose of each variable is obvious, but let’s make sure everyone understands: — total duration of a video in seconds; duration — seconds, when rewind began; initialTime — current time in seconds; currentTime — private variable which stores zoom value; _zoom — wrap on top of _zoom variable, so when new value is going to be assigned we can check whether it is in an acceptable range; zoom and — both variables define acceptable range for zoom; minZoom maxZoom — width of a line representing a specific time interval on a timeline. If zoom is not equal 1, then actual interval width equals to . Value will be used during rewind for calculations — for example, if zoom is 1, intervalWidth is 30 and intervalDuration is 15, then when user moves 10pixels left or right we will rewind by +5 or -5 seconds; intervalWidth intervalWidth * zoom — duration of an interval in seconds. If video is 55 seconds and interval is 15 seconds — then we will have 3 full intervals and one not full interval. Value will be used during rewind for calculations. intervalDuration minZoom, maxZoom, intervalWidth and intervalDuration could be constants, but I decided to make them vars — if you want to reuse this view controller with different videos you might want to adjust these values. Implement these functions: — calculates interval width depending on the zoom value; currentIntervalWidth — calculates time interval in seconds from passed width. Will be used to rewind by distance; timeIntervalFromDistance — calculates distance from given time interval. Will be used to calculate elapsed interval width; distanceFromTimeInterval — takes distance, calculates time interval from and adds this value to the current time. rewindByDistance Implement draw rect: Do not be scared. Everything here is simple :) I will explain only those things, which I think might need explanation: As you probably noticed before — when we rewind in Periscope timeline moves left-right. To have that movement we calculate value and apply to all our drawings; originX We draw first line which indicates full timeline; We draw second line which indicates elapsed time; We draw dot which indicates initial time; And last, we draw separators between intervals. And final bit on timeline view for now — implement new initializer and set to : opaque false I feel that our timeline is finished. Next stage is to add it to view controller and make it work! Add these variables: First variable is a content view, where all rewind related views/controls will be added. We will fade in and fade out this view when rewinding begins and finishes. Second variable is our actual timeline view which we implemented in a previous stage. And the last variable will help us to calculate how far finger has moved and how far we should rewind. Put this code to the end of method: loadView It adds all new subviews to their superviews and sets duration of the timeline to the duration of the asset. Update function with this code: longPressed We have added code to get current location of the finger; We calculate zoom value depending on the finger location. You can play with this calculation, but I prefer this calculation; We added logic to set initial time to the timeline when rewind begins; We call rewindByDistance method when gesture state is equal to .Changed; We fade in and fade out our newly created content view; We update previous location x if it is not equal to the current. Last stage before we try it is to layout our newly created views. Put this code to the end of : viewWillLayoutSubviews Run project on your device or simulator and check it out! That’s how it looks on my device: I really hope that you managed to do everything and it works exactly the same. If not — quickly go threw tutorial again. As you might have noticed it does not rewind when we release finger. In order to do that add update longPressed function with this code: We added 2 new lines — line 17 and line 18. With the first line we calculate new time, and with the second line we seek to that time. Run your project again.. and enjoy! It works now! Lazy people can close tutorial at this stage , because core stuff is built and next stage is mainly improvements and some nice-to-have features. (hope you are not lazy) Our goal is to add two new views: Preview image view which will show thumbnail of the video at current rewind time; Label, which will show current rewind time in format minutes:seconds. In order to update these views I decided to add closure to timeline view which will be triggered every time when value changes. currentTime Add this variable to : TimelineView Update variable implementation: currentTime Add these variables to the class: VideoViewController Add these two lines to the end of : init(videoURL: NSURL) Maximum size specifies the maximum dimensions for generated image. We want maximum height to be . Generated image is never scaled up. But in most of the cases video size is much higher than the expected thumbnail size. rewindPreviewMaxHeight Add these lines to the end of loadView method: Update function with this code: viewWillLayoutSubviews Feel free to change value. It indicates gap height between preview image view, current time label and timeline view. verticalSpacing And the last step in this section is to implement closure. Add this code just before adding timeline view to it’s superview: currentTimeDidChange We generate current time string and assign to rewindCurrentTimeLabel.text; We initialize requested time with current time and required timescale; We generate CGImages asynchronously. On completion we try to generate UIImage from CGImage, switch to main thread, set image to preview image view and ask to call layout subviews if needed. Run you project and enjoy! It looks fantastic! Let’s do the final touch! In Periscope they have a nice shadow behind preview image view. We will add it as well! Add this variable to : VideoViewController Add this block of code to the function just before setting up and adding rewindPreviewImageView to subviews : loadView (if you add it after rewindPreviewImageView, then it will be higher in a layer hierarchy and will be shown on top of imageView) For those who does not understand the purpose of line number 6 I would suggest to read about . iOS implicit animations Update viewWillLayoutSubviews: Run and check it out. Everything is great. But.. wait.. our preview image is pixelated! It is because expects us to provide pixels instead of points. Documentation on how points are different from pixels can be found . AVAssetImageGenerator.maximumSize here Let’s fix this minor issue. Update this line all over the class: with this: Now run project again. What’s going on.. Where is our shadow?! Where is our corner radius?! We forgot one little thing — returns us CGImage and we initialize UIImage with CGImage without providing required scale. In our case our needed scale is . AVAssetImageGenerator UIScreen.mainScreen().scale Find this line: And update with this: Ruuun again! Wonderful! Everything is back and it works great! Woop woop!!! Thank you for going threw this tutorial. If you liked it please press heart button and share it with your friends & colleagues. Source code from this tutorial with some improvements and more features can be downloaded . here See you in my next articles-tutorials! Bye!