Blog about joys and sorrows of programming for iOS devices, run by Milan Vít. Contains solutions for common problems iOS developers face, introduces popular and well-done libraries from third-party developers and explains thoroughly why Xcode sucks monkey balls and why you should avoid it like a plague.
PGP: D422 FA2F 0037 0ABD D1CC 22E5 4684 6F75 9ECA 2702
And now for something completely different: motion interpolation in mpv
Alright, this one is not going to be about programming, but hey – when was the last time I actually wrote anything about programming anyway, right? It's a crying shame – or at least I'd like to think it is – but programming during the day exhausts one so much that I'd have to be a supernatural being to be in a mood to write about programming in the evening, too. I mean, more supernatural than I already am, of course.
It doesn't mean it's not going to be about an interesting topic, however. The topic is motion interpolation and without going into all of the boring technical details, I'd rather just tell you what is it and why you can't live without it in 2015. You're probably well aware of the fact that most movies and TV series are still shot in 24 frames per second (which is far, far away from a high enough number) and the illusion of motion is not accomplished by rapid displaying of different images as much as it is done by utilizing an effect known as motion blur. But what would happen if we took away the motion blur and increased the number of frames per second instead?
Well, we may never know that, at least not in the foreseeable future, because getting rid of motion blur in already recorded movies would be… somewhat complicated to say the least, but we can at least artificially increase the number of frames per second. Okay, but hold on – if the missing frames are not present in the source material, where would they come from? From the aforementioned interpolation, of course!
Now, let's set up a video player to perform the real-time interpolation for you. I'll be using mpv in this tutorial as that's objectively the best video player for OS X out there (stop arguing, you're just being ridiculous with your VLC) but it might be possible to accomplish similar results with other players. But it probably isn't, ha!
We'll need to get some additional tools first – namely VapourSynth to filter the video just before mpv plays it for you and MVTools to provide the best motion interpolation filters for VapourSynth. Since we need mpv to support VapourSynth, we can't use Coalgirls' pre-built binary – we'll have to go back to the square one and compile our own binary from source. Open Terminal.app and if you don't use the Homebrew package manager already, please go sit in the corner and feel an appropriate level of embarassment. When you're back, install Homebrew with the following command:
cd /usr/local/Cellar/vapoursynth/26/lib/vapoursynth ln -s /usr/local/Cellar/mvtools/HEAD/lib/libmvtools.dylib .
And create VapourSynth configuration script in your ~/.mpv directory (create the directory with mkdir ~/.mpv if it doesn't exist). I called mine mvtools.vpy and it looks like this:
The indentation is important because… reasons. Now edit your mpv configuration file, it should reside in the ~/.config/mpv/ directory under the mpv.conf name. Make it look like this:
If you've placed the MVTools script to another location, you might want to reflect that change in the configuration file as well. Just a hint. Well, at this point you should be good to go, really – that is, unless I forgot some step along the way (let me know in the comments if that's the case). Feel free to set up mpv as your default video player as well – actually forget that, chances are it's your video player to go for a long time, right?
I'll leave with a few quick notes that I gathered during my first two days of usage:
yes, you can use it with Popcorn-Time, but you might want to add the idle=yes and keep-open=always options into your mpv configuration file;
no, it's not one hundred percent stable, you may expect issues in particular whenever a different source file is loaded within the same mkv container;
yes, you can use it to play YouTube video files as well, just brew install youtube-dl and brew reinstall ffmpeg --with-openssl; also consider adding ytdl-format=299+bestaudio/137+bestaudio/298+bestaudio/22/135+bestaudio/18/best to your mpv config file;
no, I did not report the aforementioned bug, please don't hit me reported, pull request not accepted as of yet;
yes, you could even be able to use it with Crunchyroll and other services, if you installed youtube-dl with the --with-rtmpdump option; and finally
no, you won't be able to watch any video with measly 24 FPS after this anymore, it would just feel weird.
If either of these commands fails because of a MD5/SHA1 checksum problem, I'd assume that's a temporary problem that's going to be solved in a very near future. If you don't want to wait, however, poke me in the comments for a quick 'n dirty workaround. ↩︎
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
Nifty trick: the definitive way of iterating and working with large amount of views
If you've ever had to create an application that works with large amount of data and then displays them in a screen that simulates the look and feel of classic form in its paper version, you sure know the issue. If you wish to create the form in Interface Builder (and there's no reason why you shouldn't nowadays), there's one thing you worry more than the yearly visit of your grandmother and that's an absolutely obscene amount of IBOutlets you have to create to update the views with your data.
Or do you?
In this article, I'm going to show you an incredibly simple way of accomplishing old-school form look with the minimum amount of outlets. In fact, we are not going to create any at all.
How is that possible, do you ask? Well, let me tell you that our beloved friend IBOutlet has a little, not very well known brother-in-law that's called IBOutletCollection. And what exactly does IBOutletCollection do? Well, precisely what it sounds like, it's a collection of outlets. In the code, it can be defined like this:
Mind the required strong keyword, that's a rather big and at first glance unexpected change when compared to all of our IBOutlet property lines.
After you define your IBOutletCollection in code, you can fill the NSArray it's represented with in Interface Builder, much like you would do with regular outlets. So far it's all good and dandy, so where's the catch?
Well, truth to be told, there are two, but they're both solved by using the same solution. First problem is that because you now have your views (in my case, text fields – remember, we're creating a form) in an array, how do you actually know which text field are you working with when enumerating the collection? Furthermore, do you even know the order of the collection? The sad truth is that you don't, the order of elements in outlet collection is not guaranteed and despite the fact that right now in current version of iOS, the order is quite predictable, we still have to keep the unpredictability in mind and work our way around it to ensure there won't be any unexpected issues with our code in the future. So how do we solve this?
Well, the obvious solution is assigning all of our views a tag, but while that solution truly is obvious, it's also very short-handed: using magic numbers is rarely a good idea and limiting ourselves to integer identifiers is only marginally better than shooting ourselves in the foot. Furthermore, these artificial numbers probably won't correspond to identifiers used in our data source and even if they do, what if they happen to change in the future? Are we going to assign new tags to our views? Write a class that converts identifiers in our data to the tags we came up with when designing the form?
No, I have a better solution for you and it's one I already used in my previous article: it's adding a property to existing class, in my case UITextField.
For my needs, a string identifier was the best solution, therefore my UITextField category header file gained this line of code (warning, you may want to choose a smarter variable name than I did):
@property (nonatomic, assign) NSString *id;
And its implementation file gained following line of code:
@dynamic id;
After doing that (don't forget to import the DProperty pod – if you're unsure how to do that, feel free to consult my previous article on this topic), you're able to set the value of the id variable directly from the Identity Tab in Interface Builder:
Finally, you can enumerate through your text fields (or any other views, for that matter) like this and set their text values once you obtain/parse/get the required data:
for (UITextField *textField in [self textFields]) { [textField setText:[self objectDetails][[textField id]]]; }
Long live xib/storyboard files that contain as little outlets as possible!
I needed to display data from an Excel spreadsheet in one of my latest applications and I thought that the easiest way to do that would be to write an Excel to property list convertor in .NET Framework, then load the generated property list in my iOS app, skipping the tedious and more-than-likely incorrect custom spreadsheet parsing in Objective-C. I mean, what could go wrong?
At first, everything seemed to be just fine and dandy, but then I received new spreadsheet from the customer and upon generating the property list and importing it into my application, Xcode (well, xcodebuild to be precise) left me with an incredibly helpful message that solved precisely nothing:
Error: reading plist: The data couldn't be read because it isn't in the correct format.
You might be able to spot the error if your property list is a relatively short file but mine just reached eighteen thousand lines with the latest spreadsheet from our customer and the last thing in the world you want to do is to go through the entirety of it manually. Fortunately, you don't really have to.
If you fire up Terminal.app, navigate to the folder with your property list file and run the plutil command followed by your property list filename, you get a much more precise error message:
» plutil TrialData.plist TrialData.plist: Found non-key inside <dict> at line 15392
A line number! Well, that's an information I can work with! I have no idea why Xcode doesn't run your property list through the plutil utility automatically (although it might have something to do with Xcode sucking monkey balls), but now you're able to fix your property list file anyway, without the help of Xcode. Not like Xcode is much of a help most of the time anyway.
If you've ever developed anything at all in C# and .NET Framework, you'd probably sell your family and close relatives without hesitation for having even a slight chance of being able to use two things from the Microsoft's universe in the land of Apple: the warm and welcome hug of Visual Studio and the pleasant embrace of LINQ.
As always, I have some good news for you: you can keep your relatives as I have enough “fun” with my own as it is, and you can have LINQ in Objective-C. Sounds good?
As surprising as that might seem, there's actually more than one LINQ implementation for Objective-C, but the one I have most experience with is called simply Linq to Objective-C and is developed by a wonderful person that goes by the name of Colin Eberhardt. It implements most of the LINQ queries for the NSArray and NSDictionary classes and what might seem unusual at first, all of these methods are prefixed with the linq_ keyword – well, no worries, that's definitely not some kind of mockery, the underscore is a part of convention that's actually suggested by the mighty Apple in all its glory.
Well, now there's nothing stopping you from creating awesome/terrifying methods such as this one. Except of your conscience, of course.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
Nifty trick: the definitive way of implementing tab index to text fields
In the previous article, I told you how to add a property to a category – a task that's usually not easily done. We're going to use that knowledge now to implement a proper tab index and when I say proper, I mean without the use of tags, manually setting the firstResponder (because that's about as subtle as a gynecologist wearing a gas mask) or any subclassing nonsense.
I know what you're probably thinking right now: what do I even mean by “tab index”? Has [objectively:smart] suddenly turned into a blog about guitars? Well, fear not, a trivial explanation (that you can safely skip if you've ever created a simple HTML form) says that the tab index defines the order in which various user interface elements within a given form get activated whenever user presses the Next button on the iOS keyboard. Now that I think about it, a more fitting name would be something along the lines of “next index”, but that sounds… stupid. The term “tab index” itself comes probably from the ancient times when the birthday cake of HTML forms didn't look like a brush fire yet and a tabindex attribute of a HTML tag used to define which page element would get active whenever user pressed the Tab key.
Well, enough of the history lesson, let's do some programming!
First things first, you're going to need the DProperty library1 and the UITextField extension from previous blog post, so if you haven't done so already, now would be the perfect time to read it and implement these two things. When you're finished doing that, let's enhance the view controller containing all the sneaky little text fieldses in two simple ways: first, you want the view controller to conform to the UITextFieldDelegate protocol, so you edit the header file like this:
Open your storyboard file (or xib file, grandpa) in Interface Builder and set the view controller you just edited as the delegate of all instances of UITextField that are going to have a tab index by holding down the right mouse button and dragging, just like this:
Here comes the last step, this time drag a line from each UITextField that is going to be part of the sequence to the next UITextField in line and define natural and logical order of the text fields:
And… that's about it, actually! The most elegant solution to the problem that many iOS developers face!
Technically, you don't exactly need the DProperty library if you're willing to create the accessor methods by yourself – you'd just use the
id objc_getAssociatedObject(id object, void *key) and
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) functions. ↩︎
You can copy files in Finder. You can do it in Windows Explorer. In command line, in any shell. In the most simple, stupid file manager on Linux. But apparently it is too much to ask Xcode to do such a basic task!
Nifty trick: the definitive way of adding properties to categories
This one might not seem all that useful at the first glance, I get that, but try to take it as a stepping stone to something far more awesome, something I'll describe in the next article in this series. You better look forward to it!
As you surely know, Objective-C allows you to extend existing classes without the need to subclass them by using so-called categories; however, categories can only bring you so far as they only allow you to add new methods – if you need to add new property, well, you're shit out of luck! Or… are you really?
The solution is called an associated object and if we don't want to bury ourselves in exactly one line of C code (which is one line more than I'd be comfortable with), add the very simple DProperty library into your project. If you're using CocoaPods (as you honestly should be), this process is as simple as adding the following line into your Podfile:
pod 'DProperty', '~> 0.0'
After that, there's nothing stopping you from creating a category with a similar interface…
Mind the use of the @dynamic keyword as opposed to the more-often used @sythesise one as @synthesise is just straight up not allowed in a category implementation section.
And the smarter ones already guess that the next article is going to describe the absolutely best and cleanest way of implementing a “go to next UITextField once I press the Next button on keyboard”. Oops.
Having to work in Xcode sucks as it is, but there's one thing that sucks even more – and that's having to work in Xcode while Xcode is having a seizure.
So tell me, Xcode, how the hell do you expect me to read this text, exactly?
Nifty trick: the definitive way to device detection
It sucks, but there's no avoiding it sometimes. Autolayout betrays you for quadrillionth time (on that given day), springs and struts are as much use as a one legged man at an arse kicking competition and you sigh – you know you have to fall back to the good ol' device detection macro.
But there's so many of them on the mighty Internet! Which one is the best one, which one is the fastest one? Well, fear not, I've got you covered. The following macro will tell you whether your beautiful app is running on a retina or a non-retina display (people still use these?) and, more importantly, if the device is iPhone 5 and newer or iPhone 4S and older.
Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
✓ Live Streaming✓ Interactive Chat✓ Private Shows✓ HD Quality
Anya is LIVE right now
FREE
Free to watch • No registration required • HD streaming
I don't think the timing could be any better, really – I finally “won” my domain back today which allows me to start writing this blog after a week of careful planning and choosing the best platform (and yet I picked Tumblr in the end, I know) and my latest iOS app has found its way into App Store.
But before I delve into talking about that, I'd like to (very) shortly explain what kind of posts you can expect to appear on this blog. Most of the posts are going to fall into one of these five categories: nifty tricks, dirty hacks, project spotlight, app releases and last but not least, pretty damn good reasons why Xcode sucks monkey balls and you should avoid it like a plague1. The first two categories in particular gave me the general idea of even creating a blog in the first place, as it often happens that I come up with a really cool solution to a problem, but when I need to use that solution in my next project just two weeks later, I don't even remember the basic idea. So annoying!
Anyway, with that out of the way, let's talk apps!
Well, the app I mentioned before is called Psychoid (please don't hit me, I don't particularly like the name either, but the customer is always right) and it's aimed at the students of the Department of Psychology at the Palacky University in Olomouc. The app itself provides students with up-to-date news from university, current timetable for their respective year, calendar showing all the relevant events and finally instructions how to get into every classroom. Yes, just instructions. The university didn't want us to put a map in there, so you're free to insert a snarky remark about psychology students not being able to read a simple map here: _______________. Done? Wonderful!
I coded most of the app myself, I only needed some help from my boss with the timetable part of the app. Due to this being a pretty small and simple project, I used only one external library (Tapku's unofficial iOS7 fork) to help me implement the calendar. Looking back, I probably could've used AFNetworking to simplify the network code, but oh well!
Finally, you can find Psychoid on the App Store and Google Play and the main screen looks like this. The Android version was not done by us as we don't code for inferior systems. If you excuse me now, I'll go grab my fireproof suit.
I should probably shorten the name of the last category. ↩︎