So, with the GM release of Xcode 4, we've gone ahead and ported our original EGO theme to the new Xcode 4 file format.
We're also posting a alternative version of EGO, EGOv2. It's based on EGO but has some key differences: New background color, new comment colors, themed console, and other minor tweaks and slight HSB adjustments. Here's a comparison:
Original EGO Theme |
New EGOv2 Theme |
To install the themes, run the following blocks of code in Terminal:
Original EGO
mkdir -p ~/Library/Developer/Xcode/UserData/FontAndColorThemes; cd ~/Library/Developer/Xcode/UserData/FontAndColorThemes; curl -O http://developers.enormego.com/assets/egotheme/EGO.dvtcolortheme
New EGOv2
mkdir -p ~/Library/Developer/Xcode/UserData/FontAndColorThemes; cd ~/Library/Developer/Xcode/UserData/FontAndColorThemes; curl -O http://developers.enormego.com/assets/egotheme/EGOv2.dvtcolortheme
Now just restart Xcode, go to Preferences > Fonts & Colors, and select "EGO" or "EGOv2" from the color theme drop down.
Note: Both themes now use Menlo Bold 13pt. This might not be for everyone, but I've found it much easier on the eyes and the bold seems to render nicer than regular, to me. Feel free to change!
]]>LessPHP spawns from Less.js, however we like the control/flexibility the PHP variant gave us, which is why we're using it over Less.js. In a nutshell, LESS is a CSS compiler that allows you to write CSS a bit more naturally, and then compiles down to CSS-complaint syntax. It offers numerous features such as mix-ins, variables, math, functions, etc..., but the coolest thing LESS gives you is the ability to "nest":
If you're unfamiliar with Modernizer, it's a Javascript library that will test various CSS3 features and append classes to the HTML tag. You can then target classes by calling html.borderradius a.button { }
and html.no-borderradius a.button { }
to offer an image free solution of rounded corners to browsers than support it, and then use legacy methods if the browser doesn't.
The problem with using this in combination with less is that less makes it really hard to do any sort of conditional on a parent class, once you're nested. Since you're already nested in, there's no easy way to do it other than going to the bottom of the file and creating a new nested block prefixed by html.borderradius
.
We've been adding onto LessPHP since we started using it, and I finally grew tired of the issue mentioned above. I've wanted CSS conditionals for a long time, and the complexity of LessPHP/Modernizer was unnecessary. I patched less to add a new "prepend selector", any time a selector is prepended with ^, it will automatically move that selector to the top of the scope when compiling.
Here's an example of how it works:
It's definitely a bit of a hack, but it does work. Just don't try to nest multiple prepend selectors :).
]]>Safari does offer the Web Inspect, which is nice, but a little annoying for quick look ups since everything is broken down into the DOM tree.
I've seen other Safari Extension's like BetterSource that do this right in the browser, but really, why re-invent the wheel? TextMate is an awesome editor (albeit with this issues) and would be perfect for this.
I decided to see if I could get a "View Source in TextMate" item in the contextual menu. After reading over all of the docs, it became apparent to me that opening files in a native app just wasn't going to happen.
I was able to get it to work by writing a TextMate plugin to handle the heavy lifting via it's txmt:// URL scheme. So it all worked out in the end, you'll just need to install the TextMate plugin as well as the Safari extension. Enjoy!
]]>Late last week, we had a bit of a misunderstanding with Facebook. We probably jumped the gun and ended up rendering the title of the post a bit hypocritical, as many of you pointed out. We should have reached out to Facebook first to find out how this happened, but we were pissed off and wanted to tell the world. It's not easy to get in touch with Facebook, and we were in no mood to sift through their site to figure it out. As it turns out, we could have emailed them at opensource@facebook.com.
As it turns out, Facebook didn't even know they did anything wrong. EGOTableViewPullRefresh made it's way into Three20 by way of a fork merge from a third party. This obviously doesn't excuse Facebook, since they are responsible for their repository, but it does explain how it happened.
Within hours of our post going up, Jeff and David from Facebook's Open Source team became aware of the mix up and reached out to us resolved the issue quickly. All in all, we were pretty happy with the resolution. We received credit for our code in three20, and we helped make Facebook's iPhone app just a little bit better.
Facebook didn't stop there though, David reached out to me again this week and send me a Facebook Swag Pack:
At the end of the day, Facebook screwed up, but did everything right to resolve it, and then went beyond that by sending us from free Facebook swag. We're definitely sorry for how we initially handled this situation as well, since it's clear now we could have avoided this by simply getting in touch with Facebook right away.
So thank you Facebook, you guys definitely have common courtesy!
]]>Updated 8:30PM — See below
Upon updating Facebook's iPhone app last night, the first thing I immediately noticed was Pull to Refresh, the awesome UI Element created by Loren Brichter for Tweetie 2. We fell in love with this element the second Tweetie 2 hit app store, so we re-created it and open sourced it for everyone to use.
We've seen Pull to Refresh used in a few apps before and always wondered whether or not it was the one we created, but never one as popular as Facebook. I immediately started looking to see if our code made it into their app, it would be awesome to see Facebook using something we wrote.
I was a little disappointed when I found out this element was actually apart of their open source iOS library three20. Still, I was curious as to how they did it. Was it similar to what we came up? Was it better? We were never fully happy with our implementation, but never had the time to spend on making it better.
Digging through their source code, I finally found the class: TTTableHeaderDragRefreshView. I started looking over to code to see how they accomplished it, and that's when I realized it: this was our class. You can see a diff on the two classes init methods below:
Note: For the purposes of the diff tool, I normalized the code blocks a bit, but you can compare for yourself by going here and here.
Facebook prefixed some variables, slapped their Three20 branding on it, restructured some code, but it was the same code we wrote. The same code we wrote, with zero mention of us.
Just like all of our open source code (and we've published a lot of it), our intent is always for it to be used to help developers and generally make apps/app store a better place for everyone. We were ecstatic that we might have made Facebook just a little bit better.
To find out that they took our code, re-released it as their own, and take credit for it though? That's not cool Facebook. Not cool at all. It also violates our license, which states they need to retain our copyright notice when republishing it.
Still, we're glad we could help made Facebook, three20, and all of the apps using pull to refresh in three20, better. It just would have been nice to get at least a hat tip from Facebook.
Update 8:30pm: Facebook reached out to us in the comments below and they've updated their headers to attribute the source code to us.
]]>If you're using SVN to manage your files, you'll be faced with something pretty annoying:
$ svn add Default@2x.png
svn: warning: 'Default' not found
This was incredibly frustrating for me, no matter how I tried to escape it: single quotes, double quotes, backslashes, etc. SVN refused to add.
This is due to internal path recognizers in SVN. It expects the last at symbol to specify a revision. This is easily corrected by adding an at symbol to the end of your file:
$ svn add Default@2x.png@
A (bin) Default@2x.png
You'll still need to manually add each resource, but it's better than nothing. You could also use an IDE like Cornerstone, but I prefer the SVN CLI way of managing SVN.
Before anyone says anything: I know this isn't an issue in GIT. GIT's awesome, I love GIT. Unfortunately I still have some projects that require SVN.
]]><input type="submit" value="Submit the form!" style="display: none;" />
This usually works fine, but in HTML5, it seems the spec has changed a bit and the latest Webkit (as seen in Safari 5) have changed the way they do this and actually don't respond to the enter/return key here. Apparently if the element is set to display: none, it won't respond to user events. To fix this, we just need to hide the element while still making it "visible" to user events. The following works just great:
<input type="submit" value="Submit the form!" style="position: absolute; top: 0; left: 0; z-index: 0; width: 1px; height: 1px; visibility: hidden;" />
Simple change, but it works very well.
]]>Here's a quick guide to install Clang:
alias clang_gen='rm -rf /tmp/scan-build*; xcodebuild clean; /Developer/Tools/clang/scan-build --view xcodebuild'
That's pretty much it! Just go to your project directory in Terminal now, and run "clang_gen". Once it's done running, it should open up Safari with the results.
]]>Joe's argument that being completely "without barriers to entry" worked for the Web so it should work for the iPhone platform is comparing apples to oranges. The AppStore is more than just a directory of apps, it's a platform that connects developers to customers.
1. The web is "instantly accessible." You don't "download" web sites before you get to view the content. In most cases, there's no install or setup process - Web sites are just there. If you go to a bad site, you can simply hit the back button and click on another ad infinitum. Imagine how different the web experience would be if you had to "pre-load" each site? If a user had to wait up to a minute to load a site and get it setup before looking at it. People would be much more careful about the links they clicked and search engines would be forced to have an entirely different setup for search results (user ranked sites? an approval process like the AppStore?). Throw in needing to pay $1.99 before clicking through to see if the site is what you need and it would be even more aggravating to come across a loser. Certainly, the Internet (and search space) wouldn't be as popular as they are if it worked this way but it is a good analog to the current state of the AppStore. Let's imagine that Apple did remove the review process. There are already a lot of horrible apps on the AppStore. It seems strange that Apple lets some of this stuff get through. As an entirely open platform, anyone can post to the Web - take a minute and look around; this equals a whole lot of junk sites. On the Web, it's ok because of the nature of "browsing" and the simplicity of it all. People expect to deal with it and know how to vet their own search process. The time wasted is so minuscule that it doesn't bother us; but if that same openness was used in the AppStore it would be absolute chaos - even harder to find anything worth downloading, massively time-consuming to wade through the garbage and extremely expensive to buy and trash apps that were not for you.
2. Monetization is not the same on the iPhone as it is on the Web. On the Web, the majority of revenue comes from ad dollars. Other than a few subscription offerings, the user rarely pays when they go to a site. On the iPhone, you're paying for access most of the time. Sure there are a lot of free apps, but there are also a lot of paid apps worth downloading. If you go to a crappy site, you just leave, if you download a crappy paid app, you are really pissed. Despite what those scam-ish ads say, making money on the Web is really hard. Being able to monetize your traffic using ads requires a large audience, and building a large audience requires lead time - read: the time between when you start and when you can actually collect your first check can be very long. With the AppStore, if my app gets released today for $2.99, I instantly get access to an entire community of people who want to buy it. I can start making money the second the app goes live. That "instant gratification" is important when you're trying to pay yourself and your team. Comparatively, Mac OS X developers have historically had a hard time selling their apps because of the difficulty in getting access to customers but the AppStore has fixed that. No-name developers can put out an app and it'll sell on the AppStore as long as it's good.
I hope these points make it clear that there are major differences between the AppStore and the Web, making it unfair to compare them. But I do agree with Joe that the AppStore review process is not perfect. While discarding the review process entirely would be like throwing the baby out with the bathwater, here is what I think Apple needs to fix:
1. Apple needs to make the review process much more transparent. Reasons for apps getting rejected need to be clearer and there needs to be a direct line of communication between reviewers and developers. Right now, if you want to know why your app got rejected or if you want to explain something to the reviewer that they may have missed, you can't. Sure you can email the generic AppStore email address, but getting a response takes forever (timeframe for response is generally between six months and never). When an app gets submitted a "comments" section should show up right in iTunesConnect so developers and reviewers can talk directly about the status.
2. Updates take forever to get approved, making bug fixes hard and drawn out. When I do Web development, if I find a bug, I can fix and deploy instantly. On the AppStore, you can't do that. Sometimes a very large bug appears and it's imperative that you get it fixed, waiting two weeks for a review is not efficient. The reason Apple forces updates to go through the review process at all is because they don't want to review your app, approve it and then have you submit an update that violates their terms. Updates should go through a lighter review process, just a quick check to make sure that the app is not "too different" from the original. The problem is, light reviews still take time and Apple is limited on resources. The best way to "lighten the load" is to give trust levels to developers by building a Karma system. Here's how it would work:
Some may argue that this will give long-time AppStore developers an edge, but everyone (regardless of Karma) will still need to go through the initial approval for an app they submit. The only thing changed is the time it takes to get updates approved. The other problem is that developers might get lazy since they can just "deploy again" if they need to; however, if the system is built correctly, it can prevent this. If I submit multiple updates in a 72-hour period for the same app, I'm clearly being lazy and my Karma should go down. By creating a system like this, Apple will be able to trust developers more and alleviate some of the load.
3. Planning for an app release is nearly impossible. There is no way to release an app at the EXACT moment you want. Currently, you submit, wait a few weeks and then Apple approves your app and makes it live. You have no idea when it's going live until you get that "Ready for Sale" email from Apple after you've passed approval. This makes it really hard to plan marketing campaigns to build demand. You're essentially making a "guess" and that's no way to run a business. The current method of "picking a release date" is that after you go live, you can quickly login to iTunesConnect and change the "release date" to sometime in the future. This helps, but you can only pick the date and not the time, so your app could go live anytime on that date. The problem with the current solution is that when your app is approved, it goes live unless you change the release date and if you don't get into iTunesConnect fast enough, your app will be available for a few minutes/hours. On top of that, we've found that using a release date ruins your chances of performing well in the Top Free/Paid charts because Apple uses a velocity score based on the date of approval, not the date of release. The simplest fix here is to:
With these changes, developers can plan their app launches better, making a fun business a much more viable one.
4. User ratings/reviews are pointless. While this isn't "review process" specific, it's a huge issue with the AppStore. It's no secret that the angry user speaks a lot louder and more often than the one who’s pretty happy with the product. With this in mind, you're always going to see a lot more bad reviews on apps. There are a few things Apple can do to make the system better.
I think tweaks, like those listed above, would greatly improve the Apple AppStore. People will still argue for a completely open platform but it's not fair to use an argument that compares any other platform to the AppStore. None of the open platforms out there are as good as the AppStore and none of them have a central agent that gives you as much as Apple gives you (audience for your apps, billing services, file delivery, etc.). Perhaps Apple should allow developers pining for a wholly open platform to "opt out" of the AppStore and develop apps that they would then need to market, bill and deliver all on their own (pretty much the same way that indie development is handled now on the Mac). If Apple isn't promoting the app or "endorsing it" by listing it on their AppStore, then they don't need to review it. Simple right? Sure it is, but why would they offer this? They stand a chance of losing a monopoly on the 30 percent they collect on every app sold through the AppStore, why would they just "open it up" for everyone. Would an open platform be nice? Sure. Is it necessary? Maybe. But in all reality, it doesn't make business sense for Apple.
For those who argue that all 120,000 app developers should revolt and file a class action lawsuit, I don't get where you're coming from. If it weren't for Apple, we wouldn't have any market at all. They created it, we took advantage of it, we profited and they did too. All in all, I think Apple should show good faith to their developers and make some improvements but I don't think they "owe it to us." The iPhone isn't popular because of the AppStore or any of the developers; it's popular because of Apple. I think Apple realizes the value in their developers – they have always been a company that heavily supports the development community, so I have no doubt that we'll see change. It just may take some time.
]]>At this point, hating on Apple's app approval process is old and tired. Everyone knows its broken, Apple even acknowledges it's broken. We all get rejected, usually we continue to go about our business and deal with it (maybe a bit of complaining on Twitter).
This one though, this is an entirely different situation. We just had two applications rejected for using UITouch. Here's a copy of the email we received from Apple: (Screenshot)
Please include the line below in follow-up emails for this request.
Follow-up: [REDACTED]
Dear enormego,
Thank you for submitting [REDACTED] & [REDACTED] Pro to the App Store. Unfortunately it cannot be added to the App Store because it is using a private API. Use of non-public APIs, which as outlined in the iPhone Developer Program License Agreement section 3.3.1 is prohibited:.
"3.3.1 Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs."
The non-public APIs that are included in your application are the following undocumented, private UITouch instance variables:
UITouch._locationInWindow
UITouch._phase
UITouch._previousLocationInWindow
UITouch._tapCount
UITouch._timestamp
UITouch._touchFlags
UITouch._view
UITouch._windowRegards,
iPhone Developer Program
***************************
We develop a lot for the AppStore. We have a lot of applications on AppStore, which means we've definitely experienced the cryptic and "WTF?" rejections from AppStore, that everyone else has to deal with. This one isn't cryptic at all, it's very, very specific.
Apple seems to think we're accessing it's instance variables directly. Two thoughts of this:
As to the second point, we're not modifying these ivars at run time (why would we? they're public methods). For the arguments sake, even if we were, they'd need to actually run the app, and according to our server logs, they never once opened it.
However, all of that is moot. We're not accessing the ivars directly, we're using the public Apple provided methods.
To take this a step further and try to be as transparent as possible, here is every single reference to UITouch in our application:
As you can clearly see, the bulk of the code used is from Apple's own example code. Other than that, we use it to detect the correct row index when a UIControl is tapped in a UITableViewCell. There is nothing remotely questionable here. This is standard use of UITouch, and I'm sure there are thousands of apps on AppStore that use this in the exact same way — in fact, just about all of our apps on AppStore use it this way.
We'd have no problem at all sending Apple the entire Xcode project to let them see for themselves that this isn't happening in the apps. However, there's no communication on Apple's part, as everyone knows. In fact, one of these apps showed up as Rejected on iTunesConnect 5 days ago, and we just received the email an hour ago explaining why.
This doesn't make any sense, and Apple really needs to explain itself.
Unless of course, Apple has decided that detecting touches on the iPhone is no longer allowed.
]]>The work involved in doing this on the iPhone is a pain. It's not so bad if you're viewing a single image, but if you're displaying web-based images in a table view? Ugh.
Enter EGOImageLoading.
We created EGOImageLoading to be as easy to use as HTML. EGOImageLoading is a few classes to make life easier on the developer, and to give the end user a much smoother result, easily.
Here's what EGOImageLoading does:
And here's where it gets even more awesome: It changes loading priorities on the fly. If the user scrolls a bit in the table view, it'll decrease the priority of those images, and load the ones they're looking at right now, first.
So how simple is this? You can jump to our GitHub page and grab the demo, but here's what our UITableViewCell subclass looks like:
Yeah, it really is that simple.
So how does it work?
It's all based on EGOImageLoader and EGOCache.
We published EGOCache a few months ago, and we've since improved it to work better with EGOImageLoader.
EGOImageLoader does all the heavy lifting. It checks to see if the image is cached, if it is, you get it right away. If not, it loads it up in the background, and if it's told to, it decreases the priorities and bumps new loads to the top.
You can use EGOImageLoader without table views, and it'll work great. However, this post is all about table views, so we created UIImageView and UIButton subclasses, EGOImageView and EGOButton respectively, that interact with EGOImageLoader for you. That means all you have to do is initialize them, and set the URL. Seriously, that's it.
They both contain "increaseImageLoadPriority" and "decreaseImageLoadPriority".
You'll probably never need to increase priority, since it does that for you when it loads (or re-sets a loading URL). However, you are going to want to decrease the load priority once the table view cell is moved off screen, we do this in the above example by tracking willMoveToSuperview.
So there you have it, image loading on the iPhone, that's just as easy as throwing in an HTML image tag.
You can grab EGOImageLoading on it's GitHub page: http://github.com/enormego/EGOImageLoading
Also a quick note, EGOImageLoader is powered by the awesome ASIHTTPRequest class by Ben Copsey. I'm sure you could rework it to work with NSURLConnection, but why? ASIHTTPRequest is amazing. If you're not using it, you should be.
]]>For example, our F-MyLife application leverages multiple ad networks to fill it's inventory. It currently uses the AdWhirl SDK to do this and it worked great for a while; however, our buddies over at MobClix built a much better ad aggregation platform with far more networks and we've since moved most of our traffic to MobClix so they can manage it. Since the AppStore is a pain in the ass and getting an update out would have taken weeks, we were able to allocate all of our traffic to MobClix via AdWhirl and MobClix's new platform was able to handle everything from there. We ran our ads like this for over a month and then we started noticing a dropoff in our revenue. We scrambled to find out what the problem was, but since we were filtering through data from multiple networks, it was nearly impossible to put two and two together.
We decided that in order to analyze all of this data and really see connections between everything, we were going to need to build something. So Shaun spent a few nights working on a web application that pulled in all of our data from all of our networks so it could be analyzed and displayed in a readable format along with some charts. This was a huge life saver. After reviewing the reports that we built for ourselves, we noticed a huge problem with our click-through rates and began working with everyone to resolve them.
Had we not built this tool, we probably would of never known the real cause of the issue. In fact, had we built this tool a month ago, we would of identified the bug earlier and we could have fixed it.
You don't always need to "roll your own solution" though. There are tons of products and services out there designed to fix problems just like yours, you just need to research them and set them up. A similar scenario occurred with our servers. We run FML/TWI & ProTip on Amazon's EC2 infrastructure and we monitor everything with Pingdom. The other night, we had a 6 hour downtime between 2am EDT and 8am EDT. The problem was remedied by a simple lighttpd restart; however, during that time, we lost a lot of revenue. This wasn't the first time that this happened either! So, yesterday, we setup Scoutapp to keep an eye on our server load and monitor some other metrics (like MySQL). When we woke up, we were greeted with a bunch of alerts from Scoutapp telling us that we had 1 SQL query which was taking an awfully long time to run and bogging down the server. We logged in, identified the query, figured out why it was taking so long to run and remedied the problem by adding an index to one of our tables. Done!
If it weren't for Scoutapp, we would of had to manually log into each server, every morning and manually check the slow query logs. This is painstaking and given that we're a small company with hundreds of things in the works at any given time, we don't have the free time available to check logs. Scoutapp saw a problem, let us know about it, and we fixed it. A tool saved our ass yet again.
This happens over and over in the software development business. After you're done with the product, and it makes you a little bit of money, you NEED to build or setup tools that can help you manage your new business. If you don't, you'll spend all of your time managing your first product instead of working on your next.
Heartbeat was the first tool we built to manage our AppStore products. It's been a great asset to us and after having had over a year of experience on the AppStore, we're working on Heartbeat 3.0 which will incorporate all of the knowledge that we've gained through the past year.
It seems counter-productive when you're a small company, but that's when these little things help the most. You don't have the time to do it all, so it's important to automate as much of your business as possible. Services like Pingdom and Scoutapp have helped us maintain uptime on our servers. Others like Sifter and Tender have helped us keep track of bugs and deal with support issues from our customers.
There are a ton of other products and services out there that can help you run your business. If you can't find one to meet your specific needs, spend some time, and build your own. Trust me, you'll thank yourself in the long run.
]]>When we decided to add ads to FML, we wanted to do in the least obtrusive way to the user, despite what they think in their reviews.
We originally came up with a system where it would insert the ad in the 4th or 5th row of the table view, and then just scroll. This was great for the users, but it performed horribly for us. It was pretty much never on the screen.
We changed it so it would insert it in the 3rd row, and then stick to the top, similar to how section headers work (that's actually expect how we did it). We called this implementation "sticky ads", and it's how we'll refer to it in this post. These performed incredibly well for us, and we thought it was a good mix for the user. From our stats, most people open FML frequently, so it really wouldn't get too annoying. We were wrong. The users HATED it.
The third, and current implementation of FML's ads are called "inline ads". The idea was to keep the ads on the screen as much as possible, but not permanently reduce the viewport for the user. What we did here was move the ad as the user scrolls so it appears every 3 FMLs which is approximately 1 screen length. So far, the users have responded well to these. They definitely didn't like them being stuck at the top of the screen though. The "inline ads" seem to perform much better. Since the ad is always moving on the screen, it's a bit harder for the user to completely block it out. Our CTR's have increased quite a bit with this implementation.
Due to the poor performance of the original method, we're not going to bother explaining how it works.
"Sticky Ads"
The sticky method was pretty simple, and for the most part, easy. Once you're notified by AdWhirl that the ad loaded, reload your UITableView. From there, you simply split your data set into two sections, the top section, and ad section.
From there, you add the UITableView delegate method to return a custom header view for the ad section, and return the AdWhirl ad. The only catch here is that the touches will get past to the row behind it. We prevented this by putting the ads onto a UIControl, which blocks touches from being sent to the UITableView.
View snippet on snippie: #366f1b99
You can find the full source for this method in the zip file at the bottom of the post.
"Inline Ads"
The inline ads were far more complicated. Since there's no (public) way to make the header view not-float while using UITableViewStylePlain, we divided our content up into sections of 3. Then we double the sections, to create a new section/row for every ad.
View snippet on snippie: #b716809e
We created a bunch of convenience methods to determine what row/section we were in, but it does get messy. Explaining this line by line is useless. We've attached a demo project to the bottom of this post that includes source/examples for both methods. Look over the code, specifically what's going on in the delegate/dataSource methods. If you have any questions, shoot me an email.
Demo Project / Source Code
Download the source code here: http://tr.im/tnX4
If you have any questions or need any help, shoot me an email at shaun@enormego.com
Enjoy!
]]>I've struggled with this on and off through multiple apps for over a year now, but I finally have this working smoothly.
So here's how you do it:
1. Create a category for UIAlertView to enable UITextFields:
View snippet on snippie: #1b7236b0
We override the - (id)keyboard method, so it doesn't deploy it's own keyboard, which screws things up.
2. Create your UIAlertView and add the text fields
View snippet on snippie: #538e6db1
We send becomeFirstResponder on a delay, so the alertView has a chance to start appearing, if we send it before it starts appearing, the animation is choppy or it doesn't animate at all and just appears.
3. Clean up in the delegate methods
View snippet on snippie: #d59de42c
This is pretty important, if you don't do it yourself, the keyboard gets stuck on the screen until after the alert has disappeared, which isn't ideal.
Thats it!
Keep in mind, these methods are private, which means they're subject to change. They haven't changed since iPhone OS 1.0 though.
]]>Pretty simple to implement:
View snippet on snippie: #22a22f67
You can grab it via github gist: http://gist.github.com/141427
Also to note, if the cache expired or there is no cache, it simply returns nil. So all you need to do is check if it returns something, if not, grab the data you're looking for and cache it.
]]>You can grab the code on GitHub: http://gist.github.com/122538
Here's a quick example of how to use it:
View snippet on snippie: #40922ce3
Enjoy!
P.S. EGOTitledTableViewCell has properties for titleLabel and textLabel if you want to change the font/text properties.
]]>EGODatabase uses some code from FMDB, but for the most part, it was completely reworked to use result sets and row objects. One of the biggest differences between FMDB and EGODatabase, aside from EGODatabase being thread-safe, is when selecting data, EGODatabase populates its EGODatabaseRow class with the data from SQLite, as opposed to retaining the SQLite results like FMDB does.
EGODatabase is tested to work with with iPhone OS and Mac OS X 10.5
Here's a quick look at how EGODatabase works:
View snippet on snippie: #a104ecc4
You can grab EGODatabase over at GitHub:
]]>"EGO" uses the font "DejaVu Sans Mono" at 12pt. Some of you may know it as "Panic Sans" from Panic's Coda (which is a repackaged version of it). You can grab the font over at dejavu-fonts.org. If you already have Code installed, feel free to swap in Panic Sans in place of DejaVu Sans Mono.
Here's a screenshot of "EGO" in action:
To get EGO run the following code in Terminal:
mkdir -p ~/Library/Application\ Support/Xcode/Color\ Themes; cd ~/Library/Library/Application\ Support/Xcode/Color\ Themes; curl -O http://developers.enormego.com/assets/egotheme/EGO.xccolortheme
Now just restart Xcode, go to Preferences > Fonts & Colors, and select "EGO" from the color theme drop down.
Enjoy!
UPDATE: This will only work for Xcode 3.x. Click here to get EGO for Xcode 4 and EGOv2!
]]>svn up
or git pull
to update your web server with the latest revision of your app.
/home/example.com/html/
index.php
file, all they would need to do is go to this URL:http://example.com/.svn/text-base/index.php.svn-base
That's it, there's your source code.
svn export
instead of svn checkout
or for git users, git checkout-index
instead of git pull
.
It can get a bit messy, so I usually throw the following code in my PCH file in each Xcode project:
View snippet on snippie: #edbccbd2
Now when I run into an issue in a method crashing, I can do this:
View snippet on snippie: #76f05a31
Which will then output the following:
View snippet on snippie: #dd7c2f99
The format is the method name and the line number.
Updated: An earlier version of this post used "startLineLogs(@"method name")" thanks to tips from @rob_rix and @iphonedevmag I've updated logLine to use _cmd instead, which made startLineLogs() unneeded. Thanks guys!
]]>On OSX, Apple handles issues like these with an 'autosave name' that handles everything for you automatically, it seems to be missing from the iPhone SDK, so you have to figure it out on your own.
We decided to handle the autosaving/loading in the UIApplication delegate, here's what we came up with:
View snippet on snippie: #a777a88e
This works by checking the class name of each controller, and reordering it based on that. This won't work if you use the same class for multiple UITabBarItems, but we took the assumption that you'll be using a different class for each item. This does however, account for using multiple UINavigationController's by checking the topViewController class instead of storing UINavigationController over and over again.
If you do actually use the same class name in UITabBarItems, you can switch the code above to check for the controller's tabBarItem tag value instead of the class name. We choose to use the class name so there was no extra work involved in having to set the tag value in IB.
]]>You can use a UITextView to wrap, but it doesn't have some of the properties that UILabel has, specifically shadows, and there's too much unncessary overhead there since it uses a scroll view.
I took matters into my own hands and wrote a "UILabelWrap" class. It'll wrap to as many lines as it can fit in the frame you set, and after that, it'll truncate the end.
Here's the code:
UILabelWrap.h:
View snippet on snippie: #bb41be18
UILabelWrap.m:
View snippet on snippie: #a3c942a2
Update March 10, 2009: I've updated the code above with one important bug fix and a minor compatibility fix:
We continuously keep finding great uses for this, and figured other iPhone developers would too. So we've decided to release this as an open source library for everyone.
FlickTabControl was inspired by the tab bar in Facebook's iPhone app and is incredibly easy to use. You simply add the library, switch your subclass of UITableViewController to FlickTableViewController and implement the FlickTabViewDelegate and FlickTabViewDataSource protocols, and you should be up and running.
You can grab a copy of the source here:
]]>