Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4373 articles
Browse latest View live

Video Tutorial: WatchKit Part 12: Sharing Data


Lessons Learned from Running an iOS Consultancy: Part 1

$
0
0

“Experience is the name everyone gives to their mistakes.” – Oscar Wilde

I’ve worked as an “indie” Mac and iOS developer for more than a decade.

During that time, I’ve worked alone in a basement, a home office, in traditional workspaces, with partners, with teams large and small, started three companies, and lead another.

In the process, I’ve made a lot of mistakes, and learned a ton of lessons along the way.

The most important lesson I learned is not to make the same mistake twice. This article series details some of the mistakes I made, why I made them, and how you can avoid making the same mistake in your own career.

In this first part of a 2-part series, I cover five lessons I learned from running an iOS consultancy:

  1. Learn from failure
  2. Focus early, focus often
  3. Find the right partner
  4. Be wary of investors
  5. Know when you need help

Let’s dive right in!

1) Learn from Failure

It’s no secret that people tend to learn more from their mistakes than successes. People who seem to find success in every endeavor share a common trait: they recover from failure quickly, get up and try again — often before anyone notices.

EpicFailWhether it’s software development, business management, creating products, or even personal relationships, we humans have the tendency to learn the more from our missteps than our successes.

As such, I’ve developed a hobby of reading about successful business owners such as Steve Jobs, Richard Branson, Elon Musk and Bill Gates. I’m always reading between the lines to learn the formula of grandeur behind them.

Whenever you can, try to learn from someone else’s mistakes before they become your own — whether they are mine, a family member’s or a game changer’s like Steve Jobs.

2) Focus Early, Focus Often

Before you write a line of code, seek a client, or form a partnership, you first need a focus.

I launched my first “company” during the Mac OS 8/9 era. I gathered a few friends who shared an interest in programming, and we decided to make a game. It was the late 90’s, the first tech bubble was nearing its peak and everything seemed possible.

There were four or five of us, and we loosely decided that we would all have an equal say in decisions. We didn’t even know what we were building before we agreed to build it.

Soon we discovered each of us had a different opinion and no one would agree or compromise on anything, especially when no one was making money upfront.

Confused

We finally settled on making a 2.5D fantasy game in the style of Fallout, but had no roadmap, ship dates, feature list, concept art, and no focused plan. Each of us worked on parts we felt were fun or valuable, and in the end, everything fell apart.

The biggest problem was a lack of focus and clear leadership. Since each of us was only working for future profits, there was no incentive to back down. Each of us thought his or her idea was the best.

With no leader, no money, no focus and no plan, we were just running wild. Stress levels rose and people began to focus on other projects. Soon we all abandoned the project.

The one saving grace of our failed experiment was we were all a little wiser, and for me, I understood the importance of having a focused plan.

3) Find the Right Partner

A business partnership can be a wonderful thing. He or she can motivate you when you’re exhausted, be a sounding board to bounce ideas off, boost your confidence in what you’re doing and be an anchor through the ups and downs.

However, you must choose wisely! The right business partner will force you to grow, constantly challenge you to improve and help you elevate your company to new heights. On the other hand, the wrong business partner will drain your energy, burn you out and/or damage the business and leave you holding the bag.

Friends and Family: Not the Best Partners

BeforeAfterPartnering

Partnering with friends or family might seem appealing at first, but prepare yourself for those relationships to change in ways that might not be positive. The old adage, “Never hire your friends” applies to business partners — tenfold.

Driving a business is hard enough without complicating it with personal matters that come along with working with family or friends. It’s too easy to overlook or brush aside a friend or loved one’s flaws and shortcomings, or worse yet, stuff your concerns back down inside rather than addressing them early.

However, when stress levels peak, you’ll have fewer filters and your concerns and frustrations will surface. The relationship will never be the same after that happens.

For an example, look at Penn and Teller — yes, the magicians — who have a solid partnership that spans almost 40 years. They claim their secret to a solid business partnership is not being friends. They rarely hang out outside of work and keep their personal lives separate, and it’s effective for them.

Choosing the Best Partner

Find a business partner from outside your social circles that shares your vision and values, and above all else shares your work ethic. If you are the type of person who works 16-hour days, make sure your business partner is too. When one partner works harder than the other, or at least it seems that way to one of you, the interpersonal dynamic and communication can deteriorate quickly.

You want to get it right the first time. Parting ways with a co-founder is not an easy task, especially when there’s money (and egos) on the line, so do your due diligence during vetting, and make sure to document all your agreements and plans in order to set clear expectations for each other and your business.

4) Be Wary of Investors

Finding investors for a consulting company is a hard sell, especially before you have income, products or a track record. Unless you’re lucky or have easy access to start up funds, you’ll probably need to bootstrap the company yourself.

In the event that you do have interested investors, or choose to seek investors early, make sure you understand what you’re getting into.

Understand the Investor

An investor is a business partner with a single focus — the exit. They’ll push you to be profitable and sellable early on, even if it means you have to take steps that are not in your interest, or conflict with your vision for the company.

Show_me_the_money

When you take on an investor, you trade equity in your dream for cash early on. So ask yourself: If you really believe you have a billion dollar concept, do you want to trade 30% of it for a few thousand dollars and a lot of pressure?

Later stage investors are more common for consulting companies. As you expand your business, increase your profits and experience success, investors that want a piece of the pie may approach you.

On the positive side of the equation, these people can bring more business, marketing power, advertising budgets, networking opportunities and even just good advice. However, they can also start arguments, bring woes and demand unexpected changes in direction.

Think Before You Leap

Before you dive in with an investor, it’s crucial to understand what’s in it for him or her and anticipate the inevitable exit strategy. If his or her goals and motivations don’t align with yours, there will be many bumps in the road.

A single question has helped me make tough decisions about bringing in an investor: “Is this the right move for the business, the clients and the employees?”

After carefully weighing everything, I have to be able to say yes to all three. Otherwise, the influx of capital isn’t worth the hassle.

5) Know When You Need Help

A cliché about business ownership is that owners “wear many hats.”

ManyHats

Especially in the beginning, you’re the defacto accountant, legal department, bookkeeper, human resources manager, design team, development team, manager and every other role the company needs. Likely, you’ll do a few well and the rest poorly.

Eventually, you’ll find your breaking point and you’ll have to start deciding when to hire and whom to bring onboard to fill these roles, and whether it’s better to hire vendors, contractors or employees.

Giving up profit for resources and help is never an easy decision, especially when the helpers don’t directly generate revenue. Waiting too long will bite you, and not waiting long enough will bankrupt you.

Get Help Protecting the Business

Unless you passed the bar, a lawyer is likely to be one of the first roles you need to fill. Many years ago, I thought I was smart enough to review my own contracts and that my limited cash flow shouldn’t go to a lawyer who bills $200 an hour. Like many others who’ve been in there, I made a mistake that cost me much more than a lawyer would have.

Here’s what happened: there was a clause in the contract that reduced my total payment by 5 percent for each day the project was delivered late. So if the project was 20 business days late, I would receive no payment at all.

When I negotiated the contract, I noticed the term but felt confident I would deliver on schedule. As the deadline approached, the client — who was responsible for design — had not delivered. When they finally handed it off, it was too late to finish on time.

I delivered the final product almost a month late, due to no fault of mine. Because of the wording in the contract, I received no payment and had no recourse.

For more than three months, I worked for free.

A fair contract would have set terms for both parties’ deliverables, and a good attorney would have advised me of the same. I thought I could be my own counsel, but my lack of experience and knowledge ended up creating a problem that bit me hard.

Don’t Wear More Hats Than You Can Fit on Your Head

A single line in a contract can really harm your company, be it indemnification or payment terms or other clauses.

Likewise, a miss on a line in your tax returns can cost you your entire life, and poorly composed marketing copy can make you look inept to potential clients. Really, there are endless problems that can arise from donning too many hats.

If you’re like most entrepreneurs, you’ll need to make a misstep before you realize you need help — it is a hard thing to see coming. These types of mistakes can cost you a lot, even your entire business.

Learn to admit when you’re in over your head and need help in one form or another. There is no shame in it, and you literally can’t wear all the hats, all the time – as fashionable as that may be. :]

Where To Go From Here?

I hope some of this advice has been helpful for you – hopefully you can avoid some of the mistakes I’ve made! :]

Stay tuned for the next article in the series, where I’ll cover some tips on scaling, communication, networking, and more.

In the meantime, if you have any questions, comments, or your own lessons to share, please join the forum discussion below.

Lessons Learned from Running an iOS Consultancy: Part 1 is a post from: Ray Wenderlich

The post Lessons Learned from Running an iOS Consultancy: Part 1 appeared first on Ray Wenderlich.

Lessons Learned from Running an iOS Consultancy: Part 2

$
0
0

Welcome back to my Lessons Learned from Running an iOS Consultancy article series!

In the first part of the series, I shared some lessons learned related to learning from failure, focusing, finding the right partner, investors, and more.

In this second and final part of the series, I’ll cover four more lessons:

  1. Scale smart
  2. Communicate clearly
  3. Make a good first impression
  4. Prioritize networking

Let’s dive back in!

1) Scale Smart

One of the biggest missteps in growing your business, especially a consulting company, is scaling too quickly. When a potential client approaches your small company with a large contract, it is tempting to hire new people quickly so you can fulfill it.

This can result in two problems:

  1. If the client’s project ends abruptly it can be difficult to find new work for all those people.
  2. You may be forced to carry out layoffs, which can be disastrous for morale and future growth.

Waiting too long to do this can siphon off cash reserves. On the other hand, it’s very difficult to hire the right people when you’re under pressure — you’re likely to make sacrifices you’ll regret later.

One solution is to partner with another company to fulfill the larger request and slowly scale your operation. The other is to be as careful with hiring as you are when you chose a partner.

Hiring: Choose Wisely

Hiring the right people takes a lot of time and can be a frustrating process. If you settle too early because you don’t have enough time to properly vet candidates, you’ll soon find yourself in charge of a bunch of knuckleheads.

Knuckleheads

Keep in mind that you might need to review hundreds of resumes and conduct dozens of interviews for each open position — it can take several weeks or months! Another thing to keep in mind is that if you grow the team too quickly, the culture you’re working to build might be diluted, destroyed or take on qualities you don’t wish to see.

One option is to bring on contractors instead of employees — it’s much easier to scale back contractors than it is to let employees go. In addition, working with contractors gives you the chance to evaluate their work, and in doing so, you might find the perfect people to onboard as employees when the business is ready.

Never rush a hiring decision because you need to fill a role today. In three years, you won’t care if it took a few extra weeks to find a good fit, but if you rush, you may be stuck with your subpar hire and regret your rushed decision.

Hiring: When to Cut Strings

Even if you follow strict and careful hiring procedures, you’ll make mistakes and hire people that don’t work out for one reason or another. You can only decipher so much about a person from a resume and interview(s) and at the end of the day, the decision comes down to trusting your gut.

When you do end up with someone who doesn’t pull their weight or otherwise brings down the company, you need to cut your losses and walk away.

It’s not a popular topic and letting a person go is hard for both of you. There are very few people that actually enjoy firing people, and anyone who tells you otherwise lacks empathy.

Letting someone go is especially difficult when you need to fire a friend or family member — yet another reason to avoid it. A single bad hire can bring down an entire company, and your priority always has to be the greater good. It doesn’t help anyone if you go out of business.

Letting Go

First, you need to be honest with yourself when someone isn’t a positive addition to your team. You must remember that you’re paying this person to perform a job that he or she isn’t doing to your satisfaction and depending on the scenario, might be single-handedly dragging the company down.

You must remove any personal connections or feelings you have about the worker, as disconnecting makes the process easier for both of you. Keep in mind that each of us is ultimately accountable for our actions, and that all you’re doing is holding that person accountable — it’s not a personal attack. In most cases, things are not going well for the other party either, and letting go will free both of you to pursue green pastures.

Dragging it out and trying to ignore the problem just makes things worse for both parties.

If you’re going to pull a Band-Aid off, it’s best to do it fast — this is a good metaphor to keep in mind when you need to let somebody go.

2) Communicate Clearly

Communication is paramount, plain and simple. Your clients, employees, partners and investors need to know what you expect from them, how things are progressing and most importantly when things are going wrong.

Communication With Employees

Employees must be made aware on their first day — if not during the interview process — of exactly what you expect. When they fail to meet your expectations, they should be informed and then together you should discuss how to solve the problem.

Most importantly, when they do well you should take time out of your day to let them know you’re pleased. We live in a world that forgets to say “Thank You” and recognize a job well done, so a few words of encouragement can have a positive ripple effect on morale and loyalty.

ThumbsUp

If you put off talking to an employee about a problem, you’ll grow bitter and resentful. An easily correctable problem can grow into a huge issue when you don’t address it promptly and professionally.

An added benefit is that when you address performance issues in your team, you’ll discover things about your leadership or business practices that need adjustment as well. Communication when things are going wrong is very positive for all parties involved.

Communication With Clients

Your clients will have their own expectations, and it’s important to discuss those and make sure they align with reality.

For example, consulting became much easier when I learned to under-promise to clients and set realistic expectations. Then when I over-delivered, my clients were ecstatic even if they secretly expected more of me than I promised.

The difference between delivering something two days early or two days late is huge to a client. If you are realistic with yourself, you can set realistic expectations for the client. Just do yourself a favor and always be conservative!

Communication is Key

Clients, team members and investors will never know what you’re thinking if you don’t communicate. If you’re running behind on a project, keeping it to yourself doesn’t help anyone. A quick email or phone call can clear things up with a client, reset expectations and circumvent larger issues.

Likewise, asking more from your team when things aren’t going right and being honest with them about their performance can help you exceed the client’s expectations.

Communicate frequently with everyone you work with, and above all, be honest with yourself about your feelings as these “gut instincts” are often harbingers of larger problems.

3) Make a Good First Impression

Clock

Mobile is fast-paced and sometimes too fast. The mentality that’s common to Web 2.0, “Ship now and fix later!” doesn’t work anymore. Bloggers, influencers and most especially users often give an app just one chance.

If you ship something that is incomplete, buggy or performs poorly, not only will you damage your reputation, you’ll lose market share that can be difficult to recover down the road.

As a mobile consultant or product maker, you live and die by your reputation. While it can be hard to see the wisdom in slowing down and doing things right, it’ll really make a difference.

Lesson Learned: Handshake

In 2008, I worked on an app called Handshake that eased the process of sharing contact info with another iPhone user. The app would scan the user’s address book on the first launch and compare it with the cellular number stored on the device. This allowed Handshake to detect the user, saving the user a few precious taps to select their own contact info.

The system worked great for my fellow developer and me. However, we didn’t have massive phonebooks. When the app got into the hands of bloggers who did have massive phonebooks, the process took too long and triggered WatchDog — the OS would determine the app was taking too long to launch and exit.

This simple mistake cost us positive first-day reviews and diminished our share in a market that proved hard to recover. While we thought we did thorough QA, we didn’t take the time to think through everything, and it cost us dearly.

4) Prioritize Networking

“It’s not what you know, it’s who you know.”

This adage is absolutely true. It cannot be overstated how important it is to expand your social and professional circles. A world of possibilities will open up when you procure and foster a professional network.

Not only is it important to attend CocoaHeads, meet-ups, conferences and trade shows, it’s also vital to talk to new people at these events. Everyone needs something and everyone has something to offer, often it is beneficial to do favors for people in your network. Even an act as simple as introducing one contact to another contact can pay you back exponentially.

RWTeam

Validation and Respect

You would be surprised at how often you can email someone to ask for advice or help and they happily point you in the right direction. Everyone wants validation and respect. Simply writing an email to ask how to break into a new field, or to see if your contact knows anyone looking to hire offers your contact instant validation can be a very positive experience for both of you.

You might get some sound advice you’d not find elsewhere, or learn of an opportunity nobody else knows about. Your contact might then reach out to you for help and voila, you might find your next new partner or rockstar employee.

The worst-case scenario is that your contact will be unresponsive or unable to help, and at the very least he or she will now know your name and a little bit about you.

Avoid Solitude

Even the most talented developer that works in solitude is extremely handicapped against his or her peers. I have always been an introvert, and it’s often to the detriment of my career.

It took me many years to embrace professional networking, and even now I wrestle with my natural drive to work alone. For introverts, being in social situations is exhausting, but for extroverts those same social engagements are revitalizing. Networking is equally important for both, and in some ways more important for the introvert who struggles to make connections.

Dale Carnegie wrote “How to Win Friends and Influence People” in 1936, and despite a brash title, it is the best book on working with people that I’ve encountered.

Never be afraid to meet new people, do them favors, ask for advice and take advantage of opportunities to grow your professional circle. You’ll be surprised by how often people in your network are able and willing to help, and you might also be surprised by how much you get back from helping them.

Where To Go From Here?

Running your own company is fun, rewarding, interesting and excellent for building character.

Yes, you’ll make mistakes — likely a lot of them. What’s important is that you learn from those mistakes and that you stand yourself back up after falling down. As long as you don’t repeat them and have the dedication to keep trying, you will persevere.

You can learn a lot from others, but the lessons probably won’t be as strong as those you learn for yourself. Take the opportunity to learn when you can and follow the path of those who came first, your journey will be the easier for it.

I hope this was helpful, and if you have any lessons you’ve learned that you’d like to share, please join the forum discussion below!

Lessons Learned from Running an iOS Consultancy: Part 2 is a post from: Ray Wenderlich

The post Lessons Learned from Running an iOS Consultancy: Part 2 appeared first on Ray Wenderlich.

Video Tutorial: WatchKit Part 13: Glances

Swift Style Guide: December 2014 Update

$
0
0
Check out our official Swift style guide!

Check out our latest Swift style guide update!

Swift has only been out for a few months, but it’s already gone through a ton of changes – both in terms of syntax, and what we consider best practices.

As a team, we want to have a consistent style for you in our tutorials, books, videos and conference materials, and part of that consistency is keeping up to date with the changes to the language.

As such, we’ve recently made some additions and tweaks to our raywenderlich.com Swift style guide and I wanted to highlight some of the changes.

If you left a comment, issue, or pull request – thank you! We appreciate your feedback and advice.

With that in mind, let’s take a look at what’s new!

Note: Our style guide is different than other style guides out there; we’re a tutorial site, which means our priorities are shorter and readable code for printed books and the web. Many of the decisions were made with an eye toward conserving space for print, legibility, and tutorial writing.

Optional Binding

After a surprisingly short discussion (knock on wood) we came to a consensus on two things about naming:

  1. Don’t give optional variables names like maybeView or optionalString.
  2. Don’t give unwrapped variables names like reallyLabel or unwrappedFloat.

For optional binding, we went for the simplest solution: to shadow the original name.

var textLabel: UILabel?
 
// later...
if let textLabel = textLabel {
  // do something with textLabel, which is now unwrapped
}

While let textLabel = textLabel might look a little strange, I think it’s the best solution: the variable name is short and obvious, and the type system will catch you at compile time if you mix up the optional from the unwrapped.

Structs vs Classes

In Objective-C, we pretty much used classes for everything. C-style structs became especially rare in the post-ARC world, thanks to the compiler’s need to track retains and releases for us.

Swift structs are a powerful option available to us, but there’s still some confusion over when to use a class and when to use a struct. Some people out there are suggesting, when in doubt, use a struct.

struct

However, it’s not that simple. Our ever-wise tutorial team member Matthijs added a great explanation using identity as a guide to whether something should be a struct or a class:

  • Structs have value semantics. Use structs for things that do not have an identity.


    An array that contains [a, b, c] is really the same as another array that contains [a, b, c] and they are completely interchangeable. It doesn’t matter whether you use the first array or the second, because they represent the exact same thing. That’s why arrays are structs.

  • Classes have reference semantics. Use classes for things that do have an identity or a specific life cycle.


    You would model a person as a class because two person objects are two different things. Just because two people have the same name and birthdate, doesn’t mean they are the same person. But the person’s birthdate would be a struct because a date of 3 March 1950 is the same as any other date object for 3 March 1950. The date itself doesn’t have an identity.

Struct Initializers and Constants

Like Apple, we really appreciate initializers with named parameters for clarity. From early on, our style guide expressed a preference for struct initializers over the previous C-style constructor functions for CGGeometry structs.

// preferred
var point = CGRect(x: 0, y: 10, width: 200, height: 120)
 
// not preferred
var point = CGRectMake(0, 10, 200, 120)

The struct initializer is longer (although just as easy to type with code completion) but each parameter is clearly marked.

New to the guide is a preference to constants such as CGRect.zeroRect rather than CGRectZero. For an existing variable, you can use the shorter style without the type name:

view.frame = .zeroRect

This mirrors Swift’s approach to namespacing and uses the constant that’s “included” inside the struct definition itself. We hope this makes the types easier to spot and the constants themselves cleaner-looking.

Protocol Conformance

Perhaps you’ve seen a class declaration like this before:

class MyViewController: UITableViewDelegate, UITableViewDataSource, UIScrollViewDelegate, UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate

We won’t judge you for having such a busy view controller, but we do suggest having each protocol conformance declared in its own extension:

class MyViewController {
  // Standard view controller stuff here
}
 
extension MyViewController: UITableViewDelegate {
  // Table view delegate methods here
}
 
extension MyViewController: UITextFieldDelegate {
  // Text field delegate methods here
}
 
// etc.

This keeps things organized and helps keep related methods together. It also means you can add a protocol and related methods in one step in one place, rather than editing the class declaration and then adding methods somewhere to a potentially crowded list of methods.

Where to Go From Here?

Code is executed (or compiled) by computers but read by humans” – I’ve heard variants of this quote many times, and it especially reminds me of what we do here on raywenderlich.com: our job is to communicate with you both in writing and in code.

If you learn from and enjoy our tutorials, I hope at least a small part of that is thanks to things like readability and formatting and consistency.

Swift continues to evolve, and there will continue to be strong opinions about code style. As always, we welcome your suggestions, comments, and especially pull requests at the Swift style guide repository!

Swift Style Guide: December 2014 Update is a post from: Ray Wenderlich

The post Swift Style Guide: December 2014 Update appeared first on Ray Wenderlich.

Unit Testing with Ellen Shapiro – Podcast S02 E10

$
0
0
Learn about Unit Testing with Ellen Shapiro!

Learn about Unit Testing with Ellen Shapiro!

Welcome back to season 2 of the raywenderlich.com podcast!

This is the last episode of season 2. We’re going to take a break for a few weeks, but we’ll be back with Season 3 in late January!

In this episode, we talk with Tutorial Team member and RWDevCon speaker Ellen Shapiro, to discuss Unit Testing – why you should do it, common misconceptions, and tips and tricks for Unit Testing in practice.

[Subscribe in iTunes] [RSS Feed]

Our Sponsor

  • rssheap: rssheap is a web based reader for software developers. You subscribe to tags you are interested in (iOS, Swift, Objective-C, etc.) and rssheap will find great articles for you to read. Check out their Android app too!

Interested in sponsoring a podcast episode? We sell ads via Syndicate Ads, check it out!

Links and References

Contact Us

Where To Go From Here?

We hope you enjoyed this episode of our podcast. Stay tuned for a new episode next week! :]

Be sure to subscribe in iTunes to get access as soon as it comes out!

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com!

Unit Testing with Ellen Shapiro – Podcast S02 E10 is a post from: Ray Wenderlich

The post Unit Testing with Ellen Shapiro – Podcast S02 E10 appeared first on Ray Wenderlich.

Our New Swift Books: Print Versions Now Available!

$
0
0
The print versions of our 3 new Swift books are now available!

The print versions of our 3 new Swift books are now available!

Update 12/15/14: Bumped up contest submission deadline to this Friday per a suggestion on the forums.

We have some exciting news – the print versions of our new Swift books (Swift by Tutorials, iOS 8 by Tutorials, and Core Data by Tutorials) are now available!

We always love seeing the print versions of our books, because we can finally show our families what we’ve been working so hard on :]

It’s also really cool to hold the books in your hand for the first time. The print quality turned out great, and it’s really handy to be able to flip straight to a section you’re looking for.

Keep reading for some more pictures of the new Swift books, learn about a discount for Swift by Tutorials PDF customers, and enter for a book giveaway!

The Books

The Swift books combined are pretty huge – over 1,200 pages total! Here’s a picture of what they look like next to my iPhone 5:

Side view of Swift by Tutorials, Core Data by Tutorials , and iOS 8 by Tutorials books.

Notice that iOS 8 by Tutorials is absolutely huge, while Core Data and Swift by Tutorials are about average size. This is because iOS 8 by Tutorials is meant to be an exhaustive reference, but we wanted the other two books to be more concise and to-the-point.

Here’s what the inside of the books looks like:

Inside of iOS 8 by Tutorials.

Note that on our store page, we have two options for each product:

  • Just the PDF. Nothing’s changed here – if you prefer electronic books you can buy just the PDF same way you usually would.
  • The PDF + Print Version Bundle. This gives you the best of both worlds. It also saves you a lot of money – you’re effectively getting the printed books at a big discount compared to buying them separately.

Note we don’t offer an option for just the printed books. This is because we like to keep our books as up-to-date as we can, and we want to be able to give all our customers access to these updates (in PDF form). For example, iOS Apprentice PDF customers have received 4 major updates since it was first released (from iOS 4->5->6->7->8)!

Discount for Swift by Tutorials Bundle PDF Customers – 1 Week Only

If you bought the PDF version of the Swift by Tutorials Bundle, you are eligible for a special discount on the print version, to effectively “upgrade” your order to the PDF+Print bundle.

I will send all PDF version customers an email with instructions for how to upgrade your purchase in a few minutes.

If for some reason you do not get the email, just contact me and we’ll get it sorted.

Note this discount will expire in just 1 week (22 Dec 2014), so if you’re interested snag it fast! :]

Back covers of Swift by Tutorials, iOS 8 by Tutorials, and Core Data by Tutorials.

The Giveaway

One last thing. To celebrate the launch, we’re giving away a few free Swift books!

All you have to do to enter is leave a comment on this post – this Friday we’ll choose 3 lucky winners who can each pick 1 book of their choice.

That’s it – we hope you all enjoy the print versions of Swift by Tutorials, iOS 8 by Tutorials, and Core Data by Tutorials! If you’d like to pick up a copy just check out our store page.

We hope you enjoy the print editions of our new Swift books! :]

Our New Swift Books: Print Versions Now Available! is a post from: Ray Wenderlich

The post Our New Swift Books: Print Versions Now Available! appeared first on Ray Wenderlich.

Video Tutorial: WatchKit Part 14: Handoff


HealthKit Tutorial with Swift: Getting Started

$
0
0

Learn about the new HealthKit API in iOS 8!

HealthKit is a new API in iOS 8 that provides an elegant way to store and retrieve a user’s health data.

In this HealthKit tutorial, you’ll create a simple workout tracking app. In the process, you’ll learn a ton about HealthKit, such as:

  • How to request permission to access HealthKit data
  • How to read information and format it to show it in the screen
  • How to write data back to HealthKit

Ready to get a strong HealthKit workout? Read on!

Note: To work through this tutorial, you’ll need an active iOS developer account. Without one, you won’t be able to enable the HealthKit Capability and access the HealthKit Store.

Getting Started

You’re going to build a simple app that’ll request authorization to use HealthKit, and then read and write HealthKit data. The starter project already has all the user Interface in place so you can focus on HealthKit functionality.

Download the starter project and open it in Xcode.

Build and run the app. You’ll see the guts of an app that reads and writes Workout and Quantity samples:

Base Project UI

In the next sections, you’ll build up this project so that it does all of the following:

  • Requests authorization to use HealthKit
  • Reads user characteristics
  • Reads and saves quantity samples
  • Reads and saves workouts

Before you do anything else, you must change the project Application Bundle Identifier and assign your Team.

Select HKTutorial in the Project Navigator, and then select the HKTutorial target. Select the General tab and then change the Bundle Identifier to use your own name or domain name.

Then, in the Team combo box, select the team associated with your developer account:

Change Bundle ID and Team

So far so good!

Entitlements

In order to use HealthKit, you need to add the HealthKit entitlement.

Open the Capabilities tab in the target editor, and then turn on the switch in the HealthKit section, as shown in the screenshot below:

Add HealthKit Capability

Wait for Xcode to finish configuring things for you, and once it’s done your entitlements are set up. That was easy, eh?

Permissions

Remember your app doesn’t automatically get access to HealthKit data – you need to ask for permission. That’s what you’ll do in this section.

First, open HealthManager.swift and look inside. You’ll find an empty class.

This is where you’ll add all the HealthKit related code this project needs; it will act as the gateway for other classes to interact with the HealthKit store. And good news: you’ve already got an instance of this class in all necessary view controllers, so there’s no need to set that up.

Start by importing the HealthKit framework. Still in HealthManager.swift, add the following line just below the comment block up top:

import HealthKit

The core of the HealthKit Framework is the class HKHealthStore, and you need an instance of that class as well. Insert this line inside the HealthManager:

let healthKitStore:HKHealthStore = HKHealthStore()

Now that you’ve created an instance of HKHealthStore, the next step is to request authorization to use it.

Remember how the user is the master of their data, and controls which metrics you can track? This means you don’t request global access to the HealthKit store, rather, you request access to the specific types of objects your app needs to read or write to the store.

All the object types are subclasses of HKObjectType, and it provides convenience methods to create these subclasses.

You just need to invoke one of the methods with a constant to represent the specific type requested. Below is a list of these convenience methods that cover each of the types mentioned above. Don’t do anything here in Xcode, just watch and learn:

class func quantityTypeForIdentifier(identifier: String!) -> HKQuantityType!  // to get a Quantity Type
class func categoryTypeForIdentifier(identifier: String!) -> HKCategoryType!  // to get a Category Type
class func characteristicTypeForIdentifier(identifier: String!) -> HKCharacteristicType! // to get a Characteristic type
class func correlationTypeForIdentifier(identifier: String!) -> HKCorrelationType! // to get a CorrelationType
class func workoutType() -> HKWorkoutType! // to get a Workout type

The identifier you use in these methods must be one of the pre-defined constants in HealthKit, like HKQuantityTypeIdentifierHeight for the quantity type for height measurements, or HKCharacteristicTypeIdentifierBloodType to get the characteristic type for blood type.

The workout type does not need an identifier, because it has no sub-types.

Now back to coding! Open HealthManager.swift and add this method inside the HealthManager:

func authorizeHealthKit(completion: ((success:Bool, error:NSError!) -> Void)!)
{
  // 1. Set the types you want to read from HK Store
  let healthKitTypesToRead = NSSet(array:[
    HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth),
    HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType),
    HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex),
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight),
    HKObjectType.workoutType()
    ])
 
  // 2. Set the types you want to write to HK Store
  let healthKitTypesToWrite = NSSet(array:[
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex),
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned),
    HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning),
    HKQuantityType.workoutType()
    ])
 
  // 3. If the store is not available (for instance, iPad) return an error and don't go on.
  if !HKHealthStore.isHealthDataAvailable()
  {
    let error = NSError(domain: "com.raywenderlich.tutorials.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey:"HealthKit is not available in this Device"])
    if( completion != nil )
    {
      completion(success:false, error:error)
    }
    return;
  }
 
  // 4.  Request HealthKit authorization
  healthKitStore.requestAuthorizationToShareTypes(healthKitTypesToWrite, readTypes: healthKitTypesToRead) { (success, error) -> Void in
 
    if( completion != nil )
    {
      completion(success:success,error:error)
    }
  }
}

Let’s go over the code above, step by step.

  1. You’re creating an NSSet with all the types you need to read from the HealthKit store for this tutorial. Characteristics (blood type, sex, birthday), samples (body mass, height) and workouts.
  2. You create another NSSet that includes all the types needed to write to the store (workouts, BMI, energy burned, distance).
  3. Here you check if the HealthKit store is available and return an error if it’s not. For universal apps, this is crucial because HealthKit may not be available on every device. At the time of writing, you can’t use it on an iPad.
  4. Performs the actual authorization request; it invokes requestAuthorizationToShareTypes:readTypes with the previously defined types for read and write.

Now that your code knows how to request authorization, you need to create a way for your app to invoke it.

The starter project already has an Authorize HealthKit button for this, and it invokes the method authorizeHealthKit() in MasterViewController. That sounds like the perfect place to get your app talking to itself.

Open MasterViewController.swift, locate authorizeHealthKit() and replace the line:

println("TODO: Request HealthKit authorization")

With this:

healthManager.authorizeHealthKit { (authorized,  error) -> Void in
  if authorized {
    println("HealthKit authorization received.")
  }
  else
  {
    println("HealthKit authorization denied!")
    if error != nil {
      println("\(error)")
    }
  }
}

This code invokes the request authorization from authorizeHealthKit and shows a message in the console with the result.

Build and run. Click Authorize HealthKit in the main view and you’ll see this screen pop up:

Authorize HealthKit

Turn on all the switches and click Done. You’ll see a message like this in Xcode’s console:

HealthKit authorization received.

Great! Your app has access to the store. Prepare to plunge deeper into the world of HealthKit!

Characteristics and Samples

In this section, you’ll learn:

  • How to read user characteristics
  • How to read and write samples of different types.

All the fun happens in ProfileViewController. In this view controller, you’ll read characteristics (birth day, age and blood type), and query for weight and height samples.

After that, you’ll perform a calculation with said samples (in this case, the Body Mass Index, BMI), and save that calculated sample to the Store.

Note: Body Mass Index (BMI) is a widely used indicator of body fat, and it’s calculated from the weight and height of a person. Learn more about it here.

Reading Characteristics

Before you can read user characteristics, you need to make sure the information is in the HealthKit store. Hence, you need to feed it some data.

Open the Health App on your device or simulator. Select the Health Data tab, then in the list select Me. Now click Edit and add information for the Birth Date, Biological Sex and Blood Type.

Enter whatever you like, even your ultra-clever alter ego’s stats or the kitty’s stats will do.

Add Health Data

Your next task is to build the framework to read those characteristics.

Go back to Xcode, open HealthManager.swift and add the following method to the bottom of the HealthManager class:

func readProfile() -> ( age:Int?,  biologicalsex:HKBiologicalSexObject?, bloodtype:HKBloodTypeObject?)
{
  var error:NSError?
  var age:Int?
 
  // 1. Request birthday and calculate age
  if let birthDay = healthKitStore.dateOfBirthWithError(&error)
  {
    let today = NSDate()
    let calendar = NSCalendar.currentCalendar()
    let differenceComponents = NSCalendar.currentCalendar().components(.YearCalendarUnit, fromDate: birthDay, toDate: today, options: NSCalendarOptions(0) )
    age = differenceComponents.year
  }
  if error != nil {
    println("Error reading Birthday: \(error)")
  }
 
  // 2. Read biological sex
  var biologicalSex:HKBiologicalSexObject? = healthKitStore.biologicalSexWithError(&error);
  if error != nil {
    println("Error reading Biological Sex: \(error)")
  }
  // 3. Read blood type
  var bloodType:HKBloodTypeObject? = healthKitStore.bloodTypeWithError(&error);
  if error != nil {
    println("Error reading Blood Type: \(error)")
  }
 
  // 4. Return the information read in a tuple
  return (age, biologicalSex, bloodType)
}

This method reads the user’s characteristics from the HealthKit store, and returns them in a tuple. How it works:

  1. It invokes dateOfBirthWithError() to read the birthday from HKHealthStore. The next lines perform a calendar calculation to determine age in years.
  2. biologicalSexWithError() determines biological sex.
  3. Blood type is read with the method bloodTypeWithError().
  4. Lastly, all the values return in a tuple.

If you were to build and run now, you wouldn’t see any change to the characteristics in the UI because you’ve not opened the portal for the app and store to share the data yet.

Open ProfileViewController.swift and locate updateProfileInfo().

You’ll need to invoke this method when you click on the button Read HealthKit Data. So replace the line:

println("TODO: update profile Information")

with:

let profile = healthManager?.readProfile()
 
ageLabel.text = profile?.age == nil ? kUnknownString : String(profile!.age!)
biologicalSexLabel.text = biologicalSexLiteral(profile?.biologicalsex?.biologicalSex)
bloodTypeLabel.text = bloodTypeLiteral(profile?.bloodtype?.bloodType)

This block of code invokes readProfile(), the method you just created and shows the text in the proper labels of the UI.

Interestingly, biologicalSexLiteral and bloodTypeLiteral are not actually HealthKit methods. They’re just two convenience methods — remember I said these were included — that simply return a string based on the numeric value of the blood type and biological sex.

Now you’ve got the app, characteristics and the store talking to one another, so build and run the app.

Go to the Profile & BMI view, and click Read HealthKit Data. You’ll see that the data in the table view shows whatever you entered in the Health App.

Read Characteristics

Awesome! You just read the user characteristics from the HealthKit store.

Querying Samples

Now you’re going to read the user’s weight and height, then show it in the view along with the calculated BMI based on those measurements.

To read data from the store other than characteristics, you need to use a query, specifically, the query base class HKQuery. This is an abstract class with implementations for every type of object. And in order to read samples, you’ll need to create an HKSampleQuery.

To build a query, you need:

  • To specify the type of sample you want to query for (such as weight or height).
  • An optional NSPredicate with the search conditions (such as begin and end date), and an array of NSSortDescriptors that tell the store how to order the samples.

Once you have the query, you just call the HKHealthStore method executeQuery() to get the results.

Note: If you’re familiar with Core Data, you probably noticed some similarities. An HKSampleQuery is very similar to an NSFetchedRequest for an entity type, where you specify the predicate and sort descriptors, and then ask the Object context to execute the query to get the results.

Your query should start with a generic method that reads the most recent sample of any type. This includes height and weight, as you’ll want to present the most recent measurements for these.

Open HealthManager.swift and add this method within the HealthManager class:

func readMostRecentSample(sampleType:HKSampleType , completion: ((HKSample!, NSError!) -> Void)!)
{
 
  // 1. Build the Predicate
  let past = NSDate.distantPast() as NSDate
  let now   = NSDate()
  let mostRecentPredicate = HKQuery.predicateForSamplesWithStartDate(past, endDate:now, options: .None)
 
  // 2. Build the sort descriptor to return the samples in descending order
  let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
  // 3. we want to limit the number of samples returned by the query to just 1 (the most recent)
  let limit = 1
 
  // 4. Build samples query
  let sampleQuery = HKSampleQuery(sampleType: sampleType, predicate: mostRecentPredicate, limit: limit, sortDescriptors: [sortDescriptor])
    { (sampleQuery, results, error ) -> Void in
 
      if let queryError = error {
        completion(nil,error)
        return;
      }
 
      // Get the first sample
      let mostRecentSample = results.first as? HKQuantitySample
 
      // Execute the completion closure
      if completion != nil {
        completion(mostRecentSample,nil)
      }
  }
  // 5. Execute the Query
  self.healthKitStore.executeQuery(sampleQuery)
}

To get the most recent sample, you built a query for samples ordered by date in descending order. In this case, the most recent will be the first one returned by the query.

Because you only need the first (most recent) one, you use limit to limit the number of samples returned to 1. This saves time and resources compared to returning the entire set and reducing from there.

Dive deeper into the inner workings of the query:

  1. This builds a predicate based on the date interval by using predicateForSamplesWithStartDate(_:endDate:options). Note: You’re filtering by date here for demonstration purposes – it isn’t actually required in this case and could have been set to nil.
  2. Creates the sort descriptor to return samples ordered by start date, in descending order
  3. Since you want just the latest sample, the query limit is set to 1.
  4. Builds the query object, using a passed in sample type, the predicate, the sample limit and the sort descriptor. When the query finishes, it’ll call the completion closure, where you get the first sample and call your own completion closure with the read value.
  5. Finally, the query executes.

Now you need to call this method in the UI. Open ProfileViewController.swift and add the following property definitions to ProfileViewController:

var height, weight:HKQuantitySample?

You’ll use these two HKQuantitySample properties to read the weight and height samples from the HealthStore.

Now, locate updateWeight and replace this line:

println("TODO: update Weight")

With this code:

// 1. Construct an HKSampleType for weight
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
 
// 2. Call the method to read the most recent weight sample
self.healthManager?.readMostRecentSample(sampleType, completion: { (mostRecentWeight, error) -> Void in
 
  if( error != nil )
  {
    println("Error reading weight from HealthKit Store: \(error.localizedDescription)")
    return;
  }
 
  var weightLocalizedString = self.kUnknownString;
  // 3. Format the weight to display it on the screen
  self.weight = mostRecentWeight as? HKQuantitySample;
  if let kilograms = self.weight?.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) {
    let weightFormatter = NSMassFormatter()
    weightFormatter.forPersonMassUse = true;
    weightLocalizedString = weightFormatter.stringFromKilograms(kilograms)
  }
 
  // 4. Update UI in the main thread
  dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.weightLabel.text = weightLocalizedString
    self.updateBMI()
 
  });
});

Let’s go over this section by section:

  1. First, you specify the type of quantity sample that you want to read by using quantityTypeForIdentifier (from HKSample) and you pass the identifier associated to the weight type: HKQuantityTypeIdentifierBodyMass.
  2. Then, use that sample type to call the method you just created in the HealthManager, so that the method returns samples for weight.
  3. In the completion closure, get the weight sample value in kilograms using doubleValueForUnit and then use an NSMassFormatter to transform that value into a localized text string.
  4. Updates the UI on the main thread to display the weight. HealthKit uses an internal thread, so it’s important to make sure that all UI updates happen on the main thread. You also call a method named updateBMI — this is one of those that’s included in the starter project to calculate and display Body Mass Index (more on this later).

What is this new NSMassFormater?

You introduced this new class in your latest addition to the code, and although it’s not part of HealthKit, but is closely related. iOS 8 brought this and other formatters like NSLengthFormatter and NSEnergyFormatter into the picture. They convert quantities into text strings and take the user’s location into account.

When you use them, you free yourself from localizing the strings or configuring the units of the current locale. The formatters take care of details like these.

For example, say you’re using kilograms; even if your system is not configured to use the metric system the formatter automatically converts it to the proper unit.

Now, you need to do the same for the height. Locate the method updateHeight() and replace the line:

println("TODO: update Height")

with:

// 1. Construct an HKSampleType for Height
let sampleType = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)
 
// 2. Call the method to read the most recent Height sample
self.healthManager?.readMostRecentSample(sampleType, completion: { (mostRecentHeight, error) -> Void in
 
  if( error != nil )
  {
    println("Error reading height from HealthKit Store: \(error.localizedDescription)")
    return;
  }
 
  var heightLocalizedString = self.kUnknownString;
  self.height = mostRecentHeight as? HKQuantitySample;
  // 3. Format the height to display it on the screen
  if let meters = self.height?.quantity.doubleValueForUnit(HKUnit.meterUnit()) {
    let heightFormatter = NSLengthFormatter()
    heightFormatter.forPersonHeightUse = true;
    heightLocalizedString = heightFormatter.stringFromMeters(meters);
  }
 
 
  // 4. Update UI. HealthKit use an internal queue. We make sure that we interact with the UI in the main thread
  dispatch_async(dispatch_get_main_queue(), { () -> Void in
    self.heightLabel.text = heightLocalizedString
    self.updateBMI()
  });
})

As you can see, this piece of code is almost identical to the code that reads the weight, but with two notable differences.

  1. First, the height sample type is constructed with the associated height type identifier HKQuantityTypeIdentifierHeight to allow you to read those samples.
  2. Second, it’s using an NSLengthFormatter to get a localized string from the height value, which is used to get localized strings for lengths.

Now you’ll use the weight and height you just read from the HealthKit store to calculate the BMI (Body Mass Index) and show it on the screen. Open ProfileViewController.swift and locate updateBMI().

Replace the line:

println("TODO: update BMI")

with:

if weight != nil && height != nil {
  // 1. Get the weight and height values from the samples read from HealthKit
  let weightInKilograms = weight!.quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo))
  let heightInMeters = height!.quantity.doubleValueForUnit(HKUnit.meterUnit())
  // 2. Call the method to calculate the BMI
  bmi  = calculateBMIWithWeightInKilograms(weightInKilograms, heightInMeters: heightInMeters)
}
// 3. Show the calculated BMI
var bmiString = kUnknownString
if bmi != nil {
  bmiLabel.text =  NSString(format: "%.02f", bmi!)
}

What this code does, in detail:

  1. Retrieves the double values for weight and height with the sample method doubleValueForUnits(); this is also where you specify the unit you want.

    Note: HKUnit provides methods to construct all the supported units, and here you’ve used grams for weight and meters for height. You must be careful about using compatible units, because if the requested unit is not compatible with the sample type, it’ll raise an exception. For instance, requesting a weight value using a distance unit wouldn’t work.

  2. The BMI is calculated by calling calculateBMIWithWeightInKilograms(), a utility method included in the starter project that calculates BMI from weight and height.
  3. Shows the BMI value in the corresponding label. Since BMI is just a number, you don’t need any formatter to do that.

Note: You’ll be stuck soon if you’ve not added data in the HealthKit store for the app to read. If you haven’t already, you need to create some height and weight samples at the very least.

Open the Health App, and go to the Health Data Tab. There, select the Body Measurements option, then choose Weight and then Add Data Point to add a new weight sample. Repeat the process for the Height.

Now, build and run the app. Go to the Profile & BMI view, and tap on Read HealthKit Data. If you’ve added weight and height samples in the Health App, you’ll see something like this:

Read Samples and BMI

Awesome! You just read your first samples from the HealthKit store and used them to calculate the BMI.

Saving Samples

In this section, you’ll learn how to save samples to the HealthKit store. Your test subject will be the BMI value you calculated in the previous section.

Open HealthManager.swift and add this method:

func saveBMISample(bmi:Double, date:NSDate ) {
 
  // 1. Create a BMI Sample
  let bmiType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMassIndex)
  let bmiQuantity = HKQuantity(unit: HKUnit.countUnit(), doubleValue: bmi)
  let bmiSample = HKQuantitySample(type: bmiType, quantity: bmiQuantity, startDate: date, endDate: date)
 
  // 2. Save the sample in the store
  healthKitStore.saveObject(bmiSample, withCompletion: { (success, error) -> Void in
    if( error != nil ) {
      println("Error saving BMI sample: \(error.localizedDescription)")
    } else {
      println("BMI sample saved successfully!")
    }
  })
}

Here’s what this code does:

  1. Creates a sample object using HKQuantitySample. In order to create a sample, you need:
    • A Quantity type object, likeHKQuantityType, initialized using the proper sample type. In this case, HKQuantityTypeIdentifierBodyMassIndex).
    • A Quantity object, likeHKQuantity, initialized using the passed in bmi value and the unit. In this case, since BMI is a scalar value with no units, you need to use a countUnit.
    • Start and end date, which in this case is the current date and time in both cases.
  2. HKHealthStore‘s method saveObject() is called to add the sample to the HealthKit store.

Now you’ll need to use this method in the view controller to save the BMI sample. Open ProfileViewController.swift and locate saveBMI().

Replace the line:

println("TODO: save BMI sample")

With:

// Save BMI value with current BMI value
if bmi != nil {
  healthManager?.saveBMISample(bmi!, date: NSDate())
}
else {
  println("There is no BMI data to save")
}

Quite simple – it invokes the method you just created with the BMI value and current date.

Build and run. Navigate to the Profile view and tap Read HealthKit Data to read the information, calculate the BMI and display the results. Next, tap Save BMI to save the calculated value. If everything worked, you’ll see this message in Xcode’s console:

BMI sample saved successfully!

Nicely done. Sample saved. You can verify if it’s really on the HealthKit store using the Health App. Open the Health App, and navigate to the Health Data tab. Then go to Body Measurements and then Body Mass Index.

BMI Sample in Health App

If you see something like that, you did well. It means the BMI you calculated is there, ready for the user the check it with the Health app or other third party apps!

Where To Go From Here?

Here is the example project as it stands at this point.

Important!: The completed project requires modification before you can use it with HealthKit, as it’s set to a sample bundleID. You’ll need to set it up with a bundle ID of your own, select your Team, and then turn HealthKit OFF and ON under Capabilities of the target.

See the sections on Getting Started and Entitlements and Permissions above for more detailed instructions.

Congratulations, you’ve got some hands-on experience with HealthKit! You now know how to request permissions, read characteristics, and read and write samples.

If you want to learn more, stay tuned for the next part of this HealthKit tutorial series where you’ll learn more about a more complex type of data: workouts.

In the meantime, if you have any questions or comments, please join the forum discussion below!

HealthKit Tutorial with Swift: Getting Started is a post from: Ray Wenderlich

The post HealthKit Tutorial with Swift: Getting Started appeared first on Ray Wenderlich.

HealthKit Tutorial with Swift: Workouts

$
0
0

Learn about the new HealthKit API in iOS 8!

Update 12/8/14: Updated for Xcode 6.1.1.

Welcome back to our HealthKit tutorial series!

In the first part of the series, you learned the basics of working with HealthKit: reading and writing data.

In this second and final part of the series, you’ll learn how to deal with a more complex type of data: workout data.

This project picks up where the previous tutorial left off. If you don’t have the project already, you can download it here.

Get ready to take another rep in your HealthKit workout! :]

Getting Started

In the physical realm, a workout consists of increased physical activity during a period of time. In both the digital and physical realms, you can create a workout around the following basic attributes:

  • Activity type e.g. running, cycling, curling
  • Distance
  • Start and end dates
  • Duration
  • Energy burned during the workout

In the digital realm that is HealthKit, a workout is a container for other types of samples. For instance, you could add a group of samples of your heart rate during your workout. It’s a powerful feature if you plan to work on a fitness app.

For this project, you’re going to store running workouts, but you can easily change the activity type to represent any other workout.

The starter project already contains a view controller that gives you a place to enter workout information. To see it, navigate to Workouts and then tap the + button.

NewWorkout

This view collects information and returns it to the Workouts view controller when dismissed. You’ll need to use that information to create a workout.

Saving Workouts

First, you’re going to create a method to save a running workout. Open HealthManager.swift and add this method:

func saveRunningWorkout(startDate:NSDate , endDate:NSDate , distance:Double, distanceUnit:HKUnit , kiloCalories:Double,
  completion: ( (Bool, NSError!) -> Void)!) {
 
    // 1. Create quantities for the distance and energy burned
    let distanceQuantity = HKQuantity(unit: distanceUnit, doubleValue: distance)
    let caloriesQuantity = HKQuantity(unit: HKUnit.kilocalorieUnit(), doubleValue: kiloCalories)
 
    // 2. Save Running Workout
    let workout = HKWorkout(activityType: HKWorkoutActivityType.Running, startDate: startDate, endDate: endDate, duration: abs(endDate.timeIntervalSinceDate(startDate)), totalEnergyBurned: caloriesQuantity, totalDistance: distanceQuantity, metadata: nil)
    healthKitStore.saveObject(workout, withCompletion: { (success, error) -> Void in
      if( error != nil  ) {
        // Error saving the workout
        completion(success,error)
      }
      else {
        // Workout saved
        completion(success,nil)
 
      }
    })
}

And what does this code do? Line by line, here’s the scoop:

  1. Creates the quantity objects associated to the distance and energy in the same way you created the quantity object for the BMI –using a double value and the proper unit type.
  2. An HKWorkout object is created with duration, start and end dates and the quantities you just created for energy burned and distance. Then, the workout is added to the store by invoking the saveObject method of the HealthKit Store. The result and error return during the completion closure.

Now you need to call this method in the Workouts View Controller. Open WorkoutsTableViewController.swift and locate the method unwindToSegue(). This method is invoked when you select Done on the New Workout view.

Replace the line:

println("TODO: Save workout in Health Store")

with:

if let addViewController:AddWorkoutTableViewController = segue.sourceViewController as? AddWorkoutTableViewController {
 
// 1. Set the Unit type
var hkUnit = HKUnit.meterUnitWithMetricPrefix(.Kilo)
if distanceUnit == .Miles {
  hkUnit = HKUnit.mileUnit()
}
 
// 2. Save the workout
self.healthManager?.saveRunningWorkout(addViewController.startDate!, endDate: addViewController.endDate!, distance: addViewController.distance , distanceUnit:hkUnit, kiloCalories: addViewController.energyBurned!, completion: { (success, error ) -> Void in
  if( success )
  {
    println("Workout saved!")
  }
  else if( error != nil ) {
    println("\(error)")
  }
})
}
  1. First, it creates the proper unit object. In this app, the user chooses the distance unit type from a segmented control which sets the distanceUnit property. This code checks distanceUnit to determine the proper HKUnit to use.
  2. After creating the unit, it calls the saveRunningWorkoutMethod() to store the workout with the start date, end date, duration and energy burned.

Build and run. Tap on the + button and fill in the data in the view like this:

New Workout

Wow! 26.2 miles (42.195 km) in 2 hours and 1 minute. I think you just beat the marathon world record while coding. You’re pretty talented!

Tap Done when you’ve finished. If everything works fine, you’ll see this message in Xcode console:

Workout saved!

Great! Your workout saved successfully to the HealthKit store. You can repeat this operation to add more workouts if you want.

Now it’s time to display the workouts in the table view!

Querying Workouts

If you run the app and open the Workouts View Controller, you won’t see the workouts you’ve created in the view.

You need to add the code to read and display them, and in order to read the workouts you need to create a HKSampleQuery and execute it to retrieve the data.

This will be very similar to the code used to read weight and height, so why not try writing it yourself?

Create a method in HealthManager.swift that queries for workouts of type HKWorkoutActivityType, orders them by start date descending and returns the results in a completion block. Use the following method signature:

func readRunningWorkOuts(completion: (([AnyObject]!, NSError!) -> Void)!)
Solution Inside: readRunningWorkOuts Implementation SelectShow>

You need to show the workouts in the table, so you’ll call this method and implement the table view data source next, so open WorkoutsTableViewController.swift.

You need to create an array property in this class to store the workouts. Add this code near the other property definitions at the top of WorkoutsTableViewController:

var workouts = [HKWorkout]()

Then, add this method, which will read the workouts when the view appears:

public override func viewWillAppear(animated: Bool) {
  super.viewWillAppear(animated)
 
  healthManager?.readRunningWorkOuts({ (results, error) -> Void in
    if( error != nil )
    {
      println("Error reading workouts: \(error.localizedDescription)")
      return;
    }
    else
    {
      println("Workouts read successfully!")
    }
 
    //Kkeep workouts and refresh tableview in main thread
    self.workouts = results as [HKWorkout]
    dispatch_async(dispatch_get_main_queue(), { () -> Void in
      self.tableView.reloadData()
    });
 
  })
}

This invokes readWorkouts, a method you’ve created. When the results are received, it stores them in workouts and then reloads the table view data on the main thread.

Now you need to add the table view to the datasource’s methods, so add this implementation of tableView:numberOfRowsInSection in WorkoutsTableViewController:

public override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return  workouts.count
}

This is pretty straightforward. When the table view asks for the number of rows, you just return the number of workouts you’ve read from the store.

Now it’s time to populate the table cells. You need to implement the tableView:cellForRowAtIndexPath method of the table view data source. Add this code to the WorkoutsTableViewController class:

public override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("workoutcellid", forIndexPath: indexPath) as UITableViewCell
 
 
  // 1. Get workout for the row. Cell text: Workout Date
  let workout  = workouts[indexPath.row]
  let startDate = dateFormatter.stringFromDate(workout.startDate)
  cell.textLabel!.text = startDate
 
  // 2. Detail text: Duration - Distance 
  // Duration
  var detailText = "Duration: " + durationFormatter.stringFromTimeInterval(workout.duration)!
  // Distance in Km or miles depending on user selection
  detailText += " Distance: "
  if distanceUnit == .Kilometers {
    let distanceInKM = workout.totalDistance.doubleValueForUnit(HKUnit.meterUnitWithMetricPrefix(HKMetricPrefix.Kilo))
    detailText += distanceFormatter.stringFromValue(distanceInKM, unit: NSLengthFormatterUnit.Kilometer)
  }
  else {
    let distanceInMiles = workout.totalDistance.doubleValueForUnit(HKUnit.mileUnit())
    detailText += distanceFormatter.stringFromValue(distanceInMiles, unit: NSLengthFormatterUnit.Mile)
 
  }
  // 3. Detail text: Energy Burned 
  let energyBurned = workout.totalEnergyBurned.doubleValueForUnit(HKUnit.jouleUnit())
  detailText += " Energy: " + energyFormatter.stringFromJoules(energyBurned)
  cell.detailTextLabel?.text = detailText;
 
 
  return cell
}

An explanation of the above:

  1. This gets the workout for that row and formats the startDate to show it in the cell’s text label. In order to format the date, it uses an NSDateFormatter that was pre-created for you in the starter project.
  2. Prepares a detail label string with the distance and the energyBurned. The distance displays in miles or kilometers based on the user selection (stored in the property distanceUnit). It gets the doubleValue for the distance, and passes a distance unit based on the value of that property. Then, it uses an NSDistanceFormatter to get the localized distance string by invoking the method stringFromValue:unit with the proper unit. For the duration of the workout, an NSDateComponentsFormatter is used. All these formatters are pre-created for you in the starter project.
  3. Adds the energy burned to the string using an NSEnergyFormatter. The string is finally displayed in the detailTextLabel.

Build and run the app. Navigate to Workouts, and now you should see the workouts you stored earlier in the table view:

Read workouts

Cool, you’ve got the workouts showing up as expected. Now tap on the segmented control and see how the distance shows in miles or kilometers, based on your selection.

If you go to the Health App, you won’t find this information anywhere. This is by design. The Health App only shows samples, not workouts.

However, you can let the user see information about the workouts, you’ll just need to associate some samples with them. A health-tracking app without workouts is like a trainer without a jarring scream, so you should link up the samples.

Adding Samples to Workouts

As a final step, you’ll add distance and energy burned samples to the workout.

Open HealthManager.swift and go to saveRunningWorkout(). In the return successful closure, replace these two lines:

// Workout saved
completion(success,nil)

With this :

// if success, then save the associated samples so that they appear in the HealthKit
let distanceSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning), quantity: distanceQuantity, startDate: startDate, endDate: endDate)
let caloriesSample = HKQuantitySample(type: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned), quantity: caloriesQuantity, startDate: startDate, endDate: endDate)
 
self.healthKitStore.addSamples([distanceSample,caloriesSample], toWorkout: workout, completion: { (success, error ) -> Void in
  completion(success, error)
})

At this point, you’re probably very familiar with this code, perhaps even to the point where you feel like you’re repeating yourself. But for the sake of clarity, here’s an explanation.

In the first step, it creates a quantity sample of type DistanceWalkingRunning for the running distance, and another quantity sample of type ActiveEnergyBurned for the calories consumed. Then the HealthKit store method addsamples:ToWorkout is called to add those samples to the workout.

Build and run the app, and add one or more workouts and close the app. Now that those workouts have associated samples for the distance and energy burned, you’ll be able to see them in the Health app.

Open the Health App, and go to the Health Data Tab. There, select the Fitness options. Then choose Walking+Running Distance or Active Calories to check that the data is there. You’ll see something like this:

Workouts data

Great! Now you have the ability to get important information about workouts into the HealthKit store.

Where To Go From Here?

Here you can download the project that contains all the code from this tutorial.

Important!: The completed project requires modification before you can use it with HealthKit, as it’s set to a sample bundleID. You’ll need to set it up with a bundle ID of your own, select your Team, and then turn HealthKit OFF and ON under Capabilities of the target.

See the sections on Getting Started and Entitlements and Permissions in part 1 of the series for more detailed instructions.

Hopefully this tutorial has given you some insight into the basic concepts of HealthKit and how to use them in your own apps. In order to know more about HealthKit, these are the most relevant sources:

After going through those documents and videos, you’ll be ready to dig into some more advanced aspects on HealthKit and add improvements to this app. For instance, you could add new types of samples or workouts, calculate Statistics using HKStatisticsQuery, or observe changes in the store information with HKObserverQuery.

I hope you enjoyed this tutorial, and as always if you have any questions or comments please join the forum discussion below!

HealthKit Tutorial with Swift: Workouts is a post from: Ray Wenderlich

The post HealthKit Tutorial with Swift: Workouts appeared first on Ray Wenderlich.

iOS 7 by Tutorials Second Edition Now Available!

$
0
0
iOS 7 by Tutorials fully updated for iOS 8, Xcode 6, and Swift!

iOS 7 by Tutorials fully updated for iOS 8, Xcode 6, and Swift!

This is a quick announcement to let you know some good news – the second edition of iOS 7 by Tutorials is now available!

In this new second edition, all of the tutorials in the book have been fully updated to Xcode 6 and iOS 8.

In addition, we have some great news – we have also fully ported the book to Swift!

Although this was a massive amount of work, this new second edition is completely free of charge to existing iOS 7 by Tutorials customers, to thank you for supporting this site. You can download the new second edition on your My Loot page.

If you don’t have iOS 7 by Tutorials and want to learn about some cool APIs like UIKit Dynamics, Text Kit, and background fetch – now’s a good time to grab a copy!

We hope you enjoy the new second edition!

iOS 7 by Tutorials Second Edition Now Available! is a post from: Ray Wenderlich

The post iOS 7 by Tutorials Second Edition Now Available! appeared first on Ray Wenderlich.

Video Tutorial: WatchKit Part 15: Maps

Beginning Auto Layout Tutorial in Swift: Part 1/2

$
0
0
Start thinking in auto layout constraints!

Start thinking in auto layout constraints!

Update note: This tutorial was updated to Swift and iOS8 by Brad Johnson. Original post by tutorial team member Matthijs Hollemans.

Have you ever been frustrated trying to make your apps look good in both portrait and landscape orientation? Is making screen layouts that support both the iPhone and iPad driving you to the brink of madness? Despair no longer, I bring you good news!

It’s not hard to design a user interface for a screen that is always guaranteed to be the same size, but if the screen’s frame can change, the positions and sizes of your UI elements also have to adapt to fit into these new dimensions.

The evolution of Auto Layout is quite the story. If you tried Auto Layout when it debuted with Xcode 4 and gave up, then you really should give Xcode 6 another chance. Xcode 5 and iOS 7 made it even better, and now with even more improvements in Xcode 6 and iOS 8 and the introduction of two new iPhone screen sizes, Auto Layout is a necessity in every iOS developers toolbox.

Not only does Auto Layout make it easy to support different screen sizes in your apps, as a bonus it also makes internationalization almost trivial. You no longer have to make new nibs or storyboards for every language that you wish to support, and this includes right-to-left languages such as Hebrew or Arabic.

So grab a snack and your favorite caffeinated beverage, and get ready to become an Auto Layout master!

The problem with springs and struts

Open Xcode 6 and create a new iPhone Swift project based on the Single View Application template. Call the app “StrutsProblem”:

SwiftAutoLayoutUpdate1

You are no doubt familiar with autosizing masks – also known as the “springs and struts” model. The autosizing mask determines what happens to a view when its superview changes size. Does it have flexible or fixed margins (the struts), and what happens to its width and height (the springs)?

For example, with a flexible width the view will become proportionally wider if the superview also becomes wider. And with a fixed right margin, the view’s right edge will always stick to the superview’s right edge.

The autosizing system works well for simple cases, but it quickly breaks down when your layouts become more intricate. Let’s look at an example where springs and struts simply don’t cut it.

Click on Main.storyboard to open it in Interface Builder. Before you do anything else, first disable Auto Layout and Size Classes for this storyboard. You do that in the File inspector, the first of the six tabs:

SwiftAutoLayoutUpdate4

Uncheck the Use Auto Layout box. This will tell you that size classes can’t be used either – that’s fine. Now the storyboard uses the old struts-and-springs model.

Note: Any new nib or storyboard files that you create with Xcode 4.5 or better will have Auto Layout activated by default. Because Auto Layout is an iOS 6-and-up feature only, if you want to use the latest Xcode to make apps that are compatible with iOS 5, you need to disable Auto Layout on any new nibs or storyboard files by unchecking the “Use Auto Layout” checkbox. Size Classes are compatible with iOS 7 and later.

Drag three new views onto the main view and line them up like this:

SwiftAutoLayoutUpdate5

For clarity, give each view its own color so that you can see which is which.

Each view is inset 20 points from the window’s borders; the padding between the views is also 20 points. The bottom view is 280 points wide and the two views on top are both 130 points wide. All views are 254 points high.

If you’d rather not do this by drag and drop, you can open the size inspector and enter the following values:

  • Top left: Origin 20,20, Size 130 x 254
  • Top right: Origin 170,20, Size 130 x 254
  • Bottom: Origin 20,294, Size 280 x 254

You can see what this looks like without having to run the app by using the new Preview Assistant. Open the Assistant editor, then from the top bar of the assistant editor (which will normally say “Automatic”) Choose Preview/Main Storyboard:

Opening the Preview Assistant

You can add as many simulated devices as you want to the preview assistant, which beats building and running on every different simulator! Click the + button on the bottom left to add a device. Set up two iPhone 4-inch screens, and rotate one of them to landscape using the button that appears next to the device name.

In landscape, the app doesn’t look quite right:

The wrong layout in landscape

Instead, you want the app to look like this in landscape:

What landscape is supposed to look like

Obviously, the autosizing masks for all three views leave a little something to be desired. Change the autosizing settings for the top-left view to:

SwiftAutoLayoutUpdate7

This makes the view stick to the top and left edges (but not the bottom and right edges), and resizes it both horizontally and vertically when the superview changes its size.

Similarly, change the autosizing settings for the top-right view:

SwiftAutoLayoutUpdate8

And for the bottom view:

SwiftAutoLayoutUpdate9

You’ll have seen the layout changing in the preview assistant. It will now look like this:

SwiftAutoLayoutUpdat10

Close, but not quite. The padding between the views is not correct. Another way of looking at it is that the sizes of the views are not 100% right. The problem is that the autosizing masks tell the views to resize when the superview resizes, but there is no way to tell them by how much they should resize.

You can play with the autosizing masks – for example, change the flexible width and height settings (the “springs”) – but you won’t get it to look exactly right with a 20-point gap between the three views.

Why?!?!?

To solve this layout problem with the springs and struts method, unfortunately you will have to write some code.

UIKit sends several messages to your view controllers before, during and after rotating the user interface. You can intercept these messages to make changes to the layout of your UI. Typically you would override viewWillLayoutSubviews to change the frames of any views that need to be rearranged.

Before you can do that, you first have to make outlet properties to refer to the views to be arranged.

Switch to the Assistant Editor mode (middle button on the Editor toolset on the Xcode toolbar) and Ctrl-drag from each of the three views onto ViewController.swift:

SwiftAutoLayoutUpdate11

Connect the views to these three properties, respectively:

@IBOutlet weak var topLeftView: UIView!
@IBOutlet weak var topRightView: UIView!
@IBOutlet weak var bottomView: UIView!

Add the following code to ViewController.swift:

override func viewWillLayoutSubviews() {
 
  if UIInterfaceOrientationIsLandscape(self.interfaceOrientation) {
    var rect = self.topLeftView.frame
    rect.size.width = 254
    rect.size.height = 130
    self.topLeftView.frame = rect
 
    rect = self.topRightView.frame
    rect.origin.x = 294
    rect.size.width = 254
    rect.size.height = 130
    self.topRightView.frame = rect
 
    rect = self.bottomView.frame
    rect.origin.y = 170
    rect.size.width = 528
    rect.size.height = 130
    self.bottomView.frame = rect
  }
  else {
    var rect = self.topLeftView.frame
    rect.size.width = 130
    rect.size.height = 254
    self.topLeftView.frame = rect
 
    rect = self.topRightView.frame
    rect.origin.x = 170
    rect.size.width = 130
    rect.size.height = 254
    self.topRightView.frame = rect
 
    rect = self.bottomView.frame
    rect.origin.y = 295
    rect.size.width = 280
    rect.size.height = 254
    self.bottomView.frame = rect
  }
}

This callback occurs when the view controller is rotating to a new orientation. It looks at the orientation the view controller has rotated to and resizes the views appropriately – in this case with hardcoded offsets based on the known screen dimensions of the iPhone. This callback occurs within an animation block, so the changes in size will animate.

Don’t run the app just yet. First you have to restore the autosizing masks of all three views to the following, or the autosizing mechanism will clash with the positions and sizes you set on the views in viewWillLayoutSubviews:

SwiftAutoLayoutUpdate12

That should do it. Run the app on the iPhone 5 simulator (the preview assistant doesn’t take your code into account!) and flip to landscape. Now the views line up nicely. Flip back to portrait and verify that everything looks good there as well.

It works, but that was a lot of code you had to write for a layout that is pretty simple. Imagine the effort it takes for layouts that are truly complex, especially dynamic ones where the individual views change size, or the number of subviews isn’t fixed.

Now try running the app on the 3.5-inch simulator. Whoops. The positions and sizes of the views are wrong because the hardcoded coordinates in viewWillLayoutSubviews are based on the dimensions of the 4-inch phone (320×568 instead of 320×480). You could add another if-statement that checks the screen size and uses a different set of coordinates, but you can see that this approach is becoming unworkable quickly.

Another approach you could take is to make separate nibs for the portrait and landscape orientations. When the device rotates you load the views from the other nib and swap out the existing ones. But this is still a lot of work and it adds the trouble of having to maintain two nibs instead of one. This approach is quite impractical when you’re using storyboards instead of nibs.

There must be another way

Auto Layout to the rescue!

You will now see how to accomplish this same effect with Auto Layout. First, remove viewWillLayoutSubviews from ViewController.swift, because you’re going to do this without writing any code!

Select Main.storyboard and in the File inspector panel, check both the Use Auto Layout and Use Size Classes options to enable Auto Layout and Size Classes for this storyboard file:

SwiftAutoLayoutUpdate13

A quick note on Size Classes

Size classes are a brand new and super exciting feature of iOS 8 and Xcode 6, and they make it really intuitive to only have one storyboard for universal apps. Nearly everything you see on screen can have size classes, including the screen (UIScreen), views, and your view controllers. There are two types of size classes: vertical and horizontal. Each vertical and horizontal size class can have one of three values: Regular, Compact, or Any.

The size classes correspond to the device and orientation your app is running in. For example, a portrait iPhone has a Regular height and a Compact width. The Any value is used as the generic size class value; think of it as the superclass of all the other layouts. If there is nothing defined for the size class relating to the current device and orientation, the storyboard will pull our layout from Any.

It’s easy to view and switch between size class configurations in Xcode 6. At the bottom of the storyboard, towards the middle you will see a label that says “wAny hAny“. Click on it to see the size class configuration grid:

SwiftAutoLayoutUpdate2

You can move your cursor over the other boxes in the grid to see which set of squares corresponds to which size class configuration. By default you start on Any width and Any height. This is the default, generic size class configuration. Apple recommends doing all of your initial interface layout in this class for a universal app, since all size classes will pull from this one initially. Make sure “wAny hAny” is the selected size class in your storyboard.

You will notice the size of the scenes in your story board have changed to squares to reflect our generic size class configuration.

For more detail about size classes, check out our Beginning Adaptive Layout Tutorial. This tutorial will stick to the basics of Auto Layout.

Your First Auto Layout Constraints

Look at the landscape layout in the preview assistant. It now looks like this:

SwiftAutoLayoutUpdate14

Let’s put Auto Layout into action. Hold down the key while you click on the two views on the top (the green and yellow ones), so that both are selected. From Xcode’s Editor menu, select Pin\Widths Equally:

SwiftAutoLayoutUpdate15

Select the same two views again and choose Editor\Pin\Horizontal Spacing. (Even though the two views appear selected after you carry out the first Pin action, do note that they are in a special layout relationship display mode. So you do have to reselect the two views.)

The storyboard now looks like this:

StrutsUpdate1

The orange “T-bar” shaped things represent the constraints between the views. So far you added two constraints: an Equal Widths constraint on both views (represented by the bars with the equals signs) and a Horizontal Space constraint that sits between the two views. Constraints express relationships between views and they are the primary tool you use to build layouts using Auto Layout. It might look a bit scary, but it is actually quite straightforward once you learn what it all means.

To continue building the layout for this screen, perform the following steps. Each step adds more orange T-bars. Remember to re-select the view again after adding each constraint.

For the green view on the top left, choose from the Editor\Pin menu:

  • Top Space to Superview
  • Leading Space to Superview

For the yellow view on the right, choose:

  • Top Space to Superview
  • Trailing Space to Superview

And for the big blue view at the bottom:

  • Leading Space to Superview
  • Trailing Space to Superview
  • Bottom Space to Superview

You should now have the following constraints:

StrutsUpdate2

Notice that some of the T-bars are still orange. That means your layout is incomplete; Auto Layout does not have enough constraints to calculate the positions and sizes of the views. The solution is to add more constraints until they turn blue.

Hold down and select all three views. From the Editor menu, choose Pin\Heights Equally.

Now select the top-left corner view and the bottom view (using ⌘ as before), and choose Editor\Pin\Vertical Spacing.

Interface Builder should show something like this:

StrutsUpdate3

The T-bars have become blue: great success! Auto Layout now has enough information to calculate a valid layout. It’s not quite right yet, because there’s that big space at the right of the screen which was made when you converted to the generic size class. Select the bottom view, then the trailing space constraint:

Strutsupdate4

Open the Size inspector and change the Constant value to 20:

StrutsUpdate5

Do the same for the top right view as well.

Look at the layout preview, and voila! Perfect in portrait and landscape. It also doesn’t matter which simulator you run this on; the layout works fine on 3.5-inch, 4 inch, 4.7 inch and 5.5 inch devices. Add a few devices to the preview assistant and check it out. You may also notice that “iPad” is now an option – add that as well and you’ll see that your single layout is supporting every type of device!

SwiftAutoLayout19

Cool, but what exactly did you do here? Rather than requiring you to hard-code how big your views are and where they are positioned, Auto Layout lets you express how the views in your layout relate to each other and their superview.

You have put the following relationships – what are known as constraints – into the layout:

  • The top-left and top-right views always have the same width (that was the first pin widths equally command).
  • There is a 20-point horizontal padding between the top-left and top-right views (that was the pin horizontal spacing).
  • All the views always have the same height (the pin heights equally command).
  • There is a 20-point vertical padding between the two views on top and the one at the bottom (the pin vertical spacing).
  • There is a 20-point margin between the views and the edges of the screen (the top, bottom, leading, and trailing space to superview constraints).

And that is enough to express to Auto Layout where it should place the views and how it should behave when the size of the screen changes.

Well done

You can see all your constraints in the Document Outline on the left. Xcode adds the section named Constraints when you enabled Auto Layout for the storyboard. If you don’t see the outline pane, then click the arrow button at the bottom of the Interface Builder window.

If you click on a constraint in the Document Outline, Interface Builder will highlight where it sits on the view by drawing a white outline around the constraint and adding a shadow to it so that it stands out:

SwiftAutoLayoutUpdate20

Constraints are real objects (of class NSLayoutConstraint) and they also have attributes. For example, select the constraint that creates the padding between the two top views (it is named “Horizontal Space (20)” but be sure to pick the correct one) and then switch to the Attributes inspector. There you can change the size of the margin by editing the Constant field.

SwiftAutoLayoutUpdate21

Set it to 100 and look in the Preview Assistant. Now the margin is a lot wider:

SwiftAutoLayoutUpdate22

Auto Layout is a lot more expressive than springs and struts when it comes to describing the views in your apps. In the rest of this tutorial, you will learn all about constraints and how to apply them in Interface Builder to make different kinds of layouts.

How Auto Layout Works

As you’ve seen in the test drive above, the basic tool in Auto Layout is the constraint. A constraint describes a geometric relationship between two views. For example, you might have a constraint that says:

“The right edge of label A is connected to the left edge of button B with 20 points of empty space between them.”

Auto Layout takes all of these constraints and does some mathematics to calculate the ideal positions and sizes of all your views. You no longer have to set the frames of your views yourself – Auto Layout does that for you, entirely based on the constraints you have set on those views.

Before Auto Layout, you always had to hard-code the frames of your views, either by placing them at specific coordinates in Interface Builder, by passing a rectangle into init(frame:), or by setting the view’s frame, bounds or center properties directly.

For the app that you just made, you specifically set the frames to:

Struts coordinates

You also set autosizing masks on each of these views:

Struts autosizing masks

That is no longer how you should think of your screen designs. With Auto Layout, all you need to do is this:

Auto Layout instead of struts

The sizes and positions of the views are no longer as important. Of course, when you drag a new button or label on to the canvas it will have a certain size and you will drop it at a certain position, but that is only a design aid that you use to tell Interface Builder where to put the constraints.

The idea behind auto layout is to simplify this where you set a few constants – such as the 20 point margin or maybe an exact width for an image – and then build the rest of the layout in a relative fashion.

Designing by Intent

The big advantage of using constraints is that you no longer have to fiddle with coordinates to get your views to appear in the proper places. Instead, you can describe to Auto Layout how the views are related to each other and Auto Layout will do all the hard work for you. This is called designing by intent.

When you design by intent, you’re expressing what you want to accomplish but not necessarily how it should be accomplished. Instead of saying: “the button’s top-left corner is at coordinates (20, 230)”, you now say:

“The button is centered vertically in its superview, and it is placed at a fixed distance from the left edge of the superview.”

Using this description, Auto Layout can automatically calculate where your button should appear, no matter how big or small that superview is.

Other examples of designing with intent (and Auto Layout can handle all of these instructions):

“These two text fields should always be the same size.”
“These two buttons should always move together.”
“These four labels should always be right-aligned.”

This makes the design of your user interfaces much more descriptive. You simply define the constraints, and the system calculates the frames for you automatically.

You saw in the first section that even a layout with just a few views needs quite a bit of work to layout properly in both orientations. With Auto Layout you can skip all that effort. If you set up your constraints properly, then the layout should work without any changes in both portrait and landscape.

Another important benefit of using Auto Layout is internationalization. Text in German, for example, is infamous for being very long and getting it to fit into your labels can be a headache. Again, Auto Layout takes all this work out of your hands, because it can automatically resize your labels based on the content they need to display – and have everything else adapt with constraints.

Adding support for German, French, or any other language is now simply a matter of setting up your constraints, translating the text, and… that’s it!

French

Auto Layout is not just useful for rotation; it can also easily scale your UI up and down to accommodate different screen sizes. It is no coincidence that this technology was added to iOS at the same time that the iPhone 5 and its taller screen came out, and now we have the even bigger iPhone 6 and 6 Plus!

Auto Layout makes it a lot easier to stretch your apps’ user interfaces to fill up that extra space on the larger phones. And with Dynamic Type in iOS 7 Auto Layout has become even more important. Users can now change the global text size setting — with Auto Layout this is easy to support in your own apps.

The best way to get the hang of Auto Layout is to play with it, so that’s exactly what you will do in the rest of this tutorial.

Courting constraints

Close your current project and create a new iPhone project using the Single View Application template. Name it “Constraints”.

Any new projects that you create with Xcode 6 automatically assume that you will be using Auto Layout, so you do not need to do anything special to enable it. To keep things simple though, open Main.storyboard and disable Size Classes for this project.

To start off the interface, drag a new Button onto the canvas. Notice that while you’re dragging, dashed blue lines appear. These lines are known as the guides:

SwiftAutoLayoutUpdate23

There are guides around the margins of the screen, as well as in the center:

Other examples of guides

If you have used Interface Builder before, then you have no doubt seen these guides. They are helpful hints that make it easier to align your views.

Note that when you add a new object to the view, there are no constraints! But how can this work? You just learned that Auto Layout always needs enough constraints to determine the size and position of all the views, but here you have no constraints at all. Surely this is an incomplete layout?

If you don’t supply any constraints at all, Xcode automatically assigns a set of default constraints, known as the automatic constraints. It does this at compile time when your app is built, not at design time. Auto Layout since Xcode 5 works hard to stay out of your way while you’re designing your user interfaces, and that’s just how we like it.

The automatic constraints give your views a fixed size and position. In other words, the view always has the same coordinates as you see in the storyboard. This is very handy because it means you can largely ignore Auto Layout. You simply don’t add constraints if the default ones are sufficient and only create constraints for those views that need special rules.

OK, let’s play around a bit with constraints and see what they can do. Right now, the button is in the top-left corner and has no constraints. Make sure the button is aligned with the two corner guides.

Add two new constraints to the button using the Editor\Pin menu, so that it looks like this:

SwiftAutoLayoutUpdate25

If you hadn’t guessed already, that is the Leading Space to Superview and the Top Space to Superview options.

All the constraints are also listed in the Document Outline pane on the left-hand side of the Interface Builder window:

SwiftAutoLayoutUpdate26

There are currently two constraints: a Horizontal Space between the button and the left edge of the main view, and a Vertical Space between the button and the top edge of the main view. The relationship that is expressed by these constraints is:

“The button always sits at 20 points from the top-left corner in its superview.”

Note: These aren’t actually very useful constraints to make because they’re the same as the automatic ones. If you always want your button to be relative to the top-left corner of its superview, then you might as well not provide any constraints at all and let Xcode make them for you.

Now pick up the button and place it in the scene’s top-right corner, again against the blue guides:

SwiftAutoLayoutUpdate27

Whoa, what has happened with all that angry orange? The problem here is that the size and position of the button in Interface Builder no longer correspond with the size and position that Auto Layout expects based on the constraints. This is called a misplaced view.

Run the app. The button will still appear in the top-left corner of the screen:

SwiftAutoLayoutUpdate28

When it comes to Auto Layout, orange is bad. Interface Builder drew two orange boxes: one with a dashed border and one with a solid border. The dashed box displays the view’s frame according to Auto Layout. The solid orange box is the view’s frame according to how you placed it in the scene. These two should match up, but here they don’t.

How you fix this depends on what you want to achieve:

  • Do you want the button to be attached to the left edge of the screen at a distance of 254 points? In that case you need to make the existing Horizontal Space constraint 234 points bigger. That’s what the orange badge with “+234″ means.
  • Do you want the button to be attached to the right edge of the screen instead? Then you need to remove the existing constraint and create a new one.

Delete the Horizontal Space constraint. First select it in the canvas or in the Document Outline, and then press the Delete key on your keyboard.

SwiftAutoLayoutUpdate29

Notice the orange outline around the button, and the dotted red box. The orange outline is telling you something is wrong, and the red dotted box is telling you where Auto Layout thinks this button will appear at runtime. This is happening because there are not enough constraints left to determine the complete position of the button. You still need to add a constraint for the X-position.

You may be wondering why Xcode does not add an automatic constraint for the X-position. The rule is that Xcode only creates automatic constraints if you did not set any constraints of your own. As soon as you add a single constraint, you tell Xcode that you’re now taking responsibility for this view. Xcode will no longer make any automatic constraints and expects you to add any other constraints this view needs.

Select the button and choose Editor\Pin\Trailing Space to Superview. This puts a new constraint between the right edge of the button and the right edge of the screen. This expresses the relationship:

“The button always sits at 20 points from the top-right corner in its superview.”

Run the app and rotate to landscape. Notice how the button keeps the same distance from the right screen edge:

SwiftAutoLayoutUpdate30

When you place a button (or any other view) against the guides and make a constraint, you get a spacing constraint with a standard size that is defined by Apple’s iOS Human Interface Guidelines document (also known as the “HIG”). For margins around the edges, the standard size is a space of 20 points.

Now drag the button over to the left a little:

SwiftAutoLayoutUpdate31

Again you get a dashed orange box because the view is misplaced. Let’s say this new button position is indeed what you want. It’s not uncommon to make a constraint and then nudge the view by a few pixels, making the orange boxes appear. One way to fix this is to remove the constraint and make a new one, but there is an easier solution.

The Editor menu has a Resolve Auto Layout Issues submenu. From that menu, choose Update Constraints. In my case, this tells Interface Builder it should make the constraint 64 points larger, as so:

SwiftAutoLayoutUpdate32

Great, the T-bars turn blue again and the layout is valid. In the Document Outline, you can see that the Horizontal Space constraint no longer has a standard space:

SwiftAutoLayoutUpdate33

So far you’ve played with Horizontal Space and Vertical Space constraints. There is also a “center” constraint. Drag a new Button object to the bottom center of the canvas, so that it snaps into place with the guides:

SwiftAutoLayoutUpdate34

To keep the button always center-aligned with its superview, on the horizontal axis, you need to add a Center X Alignment constraint. From the Editor menu choose Align\Horizontal Center in Container. This adds a long orange line:

SwiftAutoLayoutUpdate35

The line is orange because you’ve only specified what happens to the X-coordinate of the button, not its Y-coordinate. Use the Editor\Pin menu to add a Vertical Space constraint between the button and the bottom of the view. It should look like this:

SwiftAutoLayoutUpdate36

In case you didn’t know how, it is the Bottom Space to Superview option. The Vertical Space constraint keeps the button away from the bottom of the view (again, using the standard margin).

Run the app and rotate it to landscape. Even in landscape mode, the button stays at the bottom center of the screen:

SwiftAutoLayoutUpdate37

That’s how you express intent: “This button should always be at bottom center.” Notice that nowhere did you have to tell Interface Builder what the button’s coordinates are, only where you want it anchored in the view.

With Auto Layout, you’re no longer supposed to care about the exact coordinates of where you place your views on the canvas or what their size is. Instead, Auto Layout derives these two things from the constraints that you set.

You can see this paradigm shift in the Size inspector for the button, which is now quite different:

SwiftAutoLayoutUpdate38

With Auto Layout disabled, typing into the X, Y, Width or Height fields will change the position and size of the selected view. With Auto Layout enabled you can still type new values into these fields, but if you already have constraints set on the view it may now become misplaced. You also have to update the constraints to make them match the new values.

For example, change the Width value of the button to 100. The canvas turns into something like this:

SwiftAutoLayoutUpdate39

Xcode simply says, “It’s fine with me if you want the width to be 100 points but just so you know, that’s not what the constraints say.”

In this case you do want the button to be 100 points wide. There is a special type of constraint for this: the Fixed Width constraint. First press Undo so that the button is centered again and the T-bars are all blue. Select the button and choose Editor\Pin\Width. This puts a new T-bar below the button:

SwiftAutoLayoutUpdate40

Select that T-bar and in the Attributes inspector change Constant to 100. This forces the button to always be 100 points wide, no matter how large or small its title. To see this a bit better you can give the button a background color:

SwiftAutoLayoutUpdate41

You can also see this new Width constraint in the Document Outline on the left:

SwiftAutoLayoutUpdate42

Unlike the other constraints, which are between the button and its superview, the Width constraint only applies to the button itself. You can think of this as a constraint between the button and… the button.

You may wonder why the button did not have a Width constraint before. How did Auto Layout know how wide to make the button without it?

Here’s the thing: the button itself knows how wide it must be. It calculates this based on its title text plus some padding. If you set a background image on the button, it also takes that into account.

This is known as the intrinsic content size. Not all controls have this, but many do (UILabel is another example). If a view can calculate its own preferred size, then you do not need to set specific Width or Height constraints on it. You will see more of this later.

I am not fat

To return the button to its optimal size, first remove the Width constraint. Then select the button and choose Size to Fit Content from the Editor menu. This restores the button’s intrinsic content size.

It takes two to tango

Guides do not appear only between a view and its superview, but also between views on the same level of the view hierarchy. To demonstrate this, drag a new button onto the canvas. If you drag this button close to the others, then their guides start to interact.

Put the new button next to the existing one so that it snaps into place:

SwiftAutoLayoutUpdate43

There are quite a few dotted guidelines here. Interface Builder recognizes that these two buttons can align in different ways – at their tops, centers and baselines.

Xcode 4 would have turned one of these snapping guides into a new constraint. But since Xcode 5, if you want to have a constraint between these two buttons, you have to make it yourself. You’ve seen that you can use the Editor\Pin menu to make a constraint between two views, but there is an easier way too.

Select the new button and Ctrl-drag to the other button, like so:

SwiftAutoLayoutUpdate44

When you let go of the mouse button, a popup appears. Choose the first option, Horizontal Spacing.

SwiftAutoLayoutUpdate45

This creates a new constraint that looks like this:

SwiftAutoLayoutUpdate46

It is orange, meaning that this button needs at least one other constraint. The size of the button is known — it uses the intrinsic content size — and there is a constraint for the button’s X-position. That leaves only the Y-position without a constraint.

Here the missing constraint is pretty easy to determine but for more complicated designs it may not always be immediately obvious. Fortunately, you don’t have to guess. Xcode has been keeping score and can tell you exactly what is missing.

There is small a red arrow in the Document Outline, next to View Controller Scene. Click that arrow to see a list of all Auto Layout issues:

SwiftAutoLayoutUpdate47

Sweet! Let’s add that missing Y-position constraint. Ctrl-drag from the new button downwards to the containing view:

SwiftAutoLayoutUpdate48

The popup menu has different options this time. The items in this menu depend on the context — which views are you dragging between — and the direction you moved the mouse. Choose Bottom Space to Bottom Layout Guide.

The new button now has a Vertical Space to the bottom of the screen, but also a Horizontal Space that links it with the other button. Because this space is small (only 8 points), the T-bar may be a bit hard to see, but it is definitely there.

Click on the Horizontal Space (8) constraint in the Document Outline to select it:

SwiftAutoLayoutUpdate49

This particular constraint sits between the two buttons. What you’ve done here is say:

“The second button always appears on the left of the first one, no matter where the first button is positioned or how big it is.”

Select the button on the right and type something long into its label like “A longer label”. When you’re done, the button resizes to make room for the new text, and the other button shifts out of the way. After all, it is attached to the first button’s left edge, so that is exactly what you intended to happen:

SwiftAutoLayoutUpdate50

Just to get a better feel for how this works, play with this some more. First give the longer label button a yellow background. Drag another button into the canvas and put it above the yellow one, so that they snap into place vertically (but don’t try to align the left edges of the two buttons):

SwiftAutoLayoutUpdate51

Give the new button a background color (green) so you can more easily see its extents.

Because you snapped the two buttons together, there is now a standard space of 8 points between them that is recommended by the HIG. Turn this into a constraint by Ctrl-dragging between the two buttons. Select Vertical Spacing from the popup menu.

You are not limited to standard spacing between controls. Constraints are full-fledged objects, just like views, and therefore have attributes that you can change.

Select the Vertical Space constraint between the two buttons. You can do this in the canvas by clicking the T-bar, although that tends to be a bit finicky. By far the easiest method is to click on the constraint in the Document Outline. Once you have it selected, switch to the Attributes inspector:

SwiftAutoLayoutUpdate52

Type 40 into the Constant field to change how big the constraint is.

Run the app and flip to landscape to see the effect, or add a landscape preview assistant like you did earlier:

SwiftAutoLayoutUpdate53

The buttons certainly keep their vertical arrangement, but not their horizontal one! The reason should be obvious: the green button does not have a constraint for its X-position yet.

Adding a Horizontal Space from the green button to the left edge of the canvas won’t solve this problem. With such a constraint the green button always keeps the same X-coordinate, even in landscape. That doesn’t look very nice, so instead you are going to express the following intention:

“The yellow button will always be horizontally centered, and the green button will align its left edge with the left edge of the yellow button.”

You already have a constraint for the first condition, but not for the second. Interface Builder shows guides for alignment, so you can drag the top button until its left edge snaps with the yellow button:

SwiftAutoLayoutUpdate55

Finally, Ctrl-drag between the two buttons and from the popup menu choose Left. This creates an alignment constraint that says: “The left edges of these two views are always aligned”. In other words, the two buttons will always have the exact same X-position. That solves the layout problem and the T-bars turn blue:

SwiftAutoLayoutUpdate56

Run the app and rotate to landscape or check the preview assistant to verify that it works:


SwiftAutoLayoutUpdate57

Where To Go From Here?

Now that you’ve got your first taste of Auto Layout, how do you like it? It can take a bit of getting used to, but can make your life a lot easier and your apps much more flexible!

Want to learn more? Stay tuned for part 2 of this Auto Layout tutorial, where you’ll continue playing with the buttons in Interface Builder to get a better understanding of the possibilities Auto Layout offers — and the problems you may encounter.

And best of all – you will also use Auto Layout to create a realistic layout that you may find in a real app! :]

In the meantime, if you have any questions or comments please join the forum discussion below!

Beginning Auto Layout Tutorial in Swift: Part 1/2 is a post from: Ray Wenderlich

The post Beginning Auto Layout Tutorial in Swift: Part 1/2 appeared first on Ray Wenderlich.

Beginning Auto Layout Tutorial in Swift: Part 2/2

$
0
0
Start thinking in auto layout constraints!

Start thinking in auto layout constraints!

Update note: This tutorial was updated to Swift and iOS8 by Brad Johnson. Original post by tutorial team member Matthijs Hollemans.

In part 1 of this Auto Layout tutorial you saw that the old “springs-and-struts” model for making user interfaces cannot easily solve all layout problems. Auto Layout is the solution, but because this technology is so powerful it is also a bit more tricky to use.

Thankfully, Xcode 5 made Auto Layout a lot easier. If you tried Auto Layout in Xcode 4 and gave up, then we invite you to give it another try with Xcode 6.

In this second part and final part of the Auto Layout tutorial series, you’ll continue learning all about constraints and how to apply them!

A little runtime excursion

This Auto Layout tutorial begins with a very simple app that looks like this:
AutoLayoutSwiftUpdate1

It has two buttons that have their background color set just so it’s clearer to see their boundaries. The buttons have a number of constraints between them. If you’ve been following along with the previous part you can continue using your existing app. Simply remove the other two buttons from the canvas.

If you’re starting from scratch, create a new Swift iPhone application using the Single View Application template. Drag two buttons into the scene and give them a background color. Use the Editor\Pin menu to make a Vertical Spacing constraint between the two buttons (40 points), and a Bottom Space to Superview constraint on the lower button (20 points). Use the Editor\Align menu to center the yellow button horizontally in the container, and again to align the left edges of both buttons.

Playing with this in Interface Builder is all well and good, but let’s see how this works at runtime. Add the following method to ViewController.swift:

@IBAction func buttonTapped(sender: UIButton) {
  if sender.titleForState(.Normal) == "X" {
    sender.setTitle("A very long title for this button", forState: .Normal)
  } else {
    sender.setTitle("X", forState: .Normal)
  }
}

This toggles between a long title and a short title for the button that triggered the event. Connect this action method to both of the buttons in Interface Builder. Ctrl-drag from each button to the view controller and select buttonTapped: in the popup.

Run the app and tap the buttons to see how it behaves. Perform the test in both portrait and landscape orientations.

AutoLayoutSwiftUpdate2

Regardless of which button has the long title and which has the short title, the layout always satisfies the constraints you have given it:

  • The lower button is always center-aligned in the window, horizontally.
  • The lower button always sits 20 points from the bottom of the window.
  • The top button is always left-aligned with the lower button and 40 points above it.

That is the entire specification for your user interface.

For fun, remove the Leading Alignment constraint (select it in the outline pane and press Delete on your keyboard), then select both buttons in Interface Builder and from the Align menu pick Right Edges. Now run the app again and notice the differences.

Repeat, but now choose Align\Horizontal Centers. That will always center the top button with respect to the bottom button. Run the app and see how the buttons act when you tap them. (Remember, if you get a dashed orange box when you change the constraints, you can use the Editor\Resolve Auto Layout Issues menu to update the button frames accordingly.)

Fixing the width

The Pin menu has an option for Widths Equally. If you set this constraint on two views, then Auto Layout will always make both views equally wide, based on which one is the largest. Let’s play with that for a minute.

Select both buttons and choose Editor\Pin\Widths Equally. This adds a new constraint to both buttons:

AutoLayoutSwiftUpdate3

You have seen this type of constraint before, in the first part of this tutorial. It looks like the usual T-bar but in the middle it has a circle with an equals sign.

Even though there are two T-bars, in the Document Outline this shows up as a single Equal Widths constraint:

AutoLayoutSwift4

Changing the label text on one button will now change the size of the other one as well. Change the bottom button’s label to “X”, just to make it really small. You will notice that the top button no longer fits its text:

AutoLayoutSwiftUpdate5

So how does Auto Layout know which button’s size to use for both of them? If you pay close attention, you’ll see that the top button’s frame is no longer correct:

AutoLayoutSwiftUpdate6

Obviously this is not what you want, so select the top button and choose Size to Fit Content from the Editor menu (or press ⌘ =). Now the text fits inside the button again – or rather, the button fits around the text – and due to the Equal Widths constraint the yellow button also resizes.

Run the app and tap the buttons. The buttons always have the same width, regardless of which one has the largest label:

AutoLayoutSwiftUpdate7

Of course, when both labels are very short, both buttons will shrink equally. After all, unless there is a constraint that prevents it, buttons will size themselves to fit their content exactly, no more, no less. What was that called again? Right, the intrinsic content size.

Intrinsic Content Size

Before Auto Layout, you always had to tell buttons and other controls how big they should be, either by setting their frame or bounds properties or by resizing them in Interface Builder. But it turns out that most controls are perfectly capable of determining how much space they need, based on their content.

A label knows how wide and tall it is because it knows the length of the text that has been set on it, as well as the font size for that text. Likewise for a button, which might combine the text with a background image and some padding.

The same is true for segmented controls, progress bars, and most other controls, although some may only have a predetermined height but an unknown width.

This is known as the intrinsic content size, and it is an important concept in Auto Layout. You have already seen it in action with the buttons. Auto Layout asks your controls how big they need to be and lays out the screen based on that information.

Usually you want to use the intrinsic content size, but there are some cases where you may not want to do that. You can prevent this by setting an explicit Width or Height constraint on a control.

Imagine what happens when you set an image on a UIImageView if that image is much larger than the screen. You usually want to give image views a fixed width and height and scale the content, unless you want the view to resize to the dimensions of the image.

So what happens when one of the buttons has a fixed Width constraint on it? Buttons calculate their own size, but you can override this by giving them a fixed width. Select the top button and choose Pin\Width from the menu. This adds a solid T-bar below the button:

AutoLayoutSwiftUpdate10

Because this sort of constraint only applies to the button itself, not to its superview, it is listed in the Document Outline below the button object. In this case, you have fixed the button to a width of 46 points:

AutoLayoutSwiftUpdate11

 

You cannot simply drag the button’s resize handles to make the button wider. If you do, you’ll end up with a whole bunch of orange boxes. Remember that Xcode 6 does not automatically update the constraints for you (unlike Xcode 4). So if you make a change to the button’s frame, it’s up to you to make the constraints match again. The alternative approach is to simply change the constraint instead.

Select the Width constraint and go to the Attributes inspector. Change Constant to 80 to make the button wider:

AutoLayoutSwiftUpdate12

Run the app and tap the buttons. What happens? The button text does change, but it gets truncated because there is not enough room:

AutoLayoutSwiftUpdate13

Because the top button has a fixed-width constraint and both buttons are required to be the same size, they will never shrink or grow.

Note: You probably wouldn’t set a Width constraint on a button by design – it is best to let the button use its intrinsic size – but if you ever run into a layout problem where you expect your controls to change size and they don’t, then double check to make sure a fixed Width constraint didn’t sneak in there.

Play around with this stuff for a bit to get the hang of pinning and aligning views. Get a feel for it, because not everything is immediately obvious. Just remember that there must always be enough constraints so that Auto Layout can determine the position and size for all views.

Got enough constraints

Gallery example

You should now have an idea of what constraints are and how you can build up your layouts by forging relationships between the different views. In the following sections, you will see how to use Auto Layout and constraints to create layouts that meet real-world scenarios.

Let’s pretend you want to make an app that has a gallery of your favorite programmers. It looks like this in portrait and landscape:

The Gallery app

The screen is divided into four equal quarters. Each quarter has an image view and a label. How would you approach this?

Let’s start by setting up the basic app. Create a new Swift iPhone project using the Single View Application template and name it “Gallery”.

Open Main.storyboard. Disable Size classes, because for the purposes of this tutorial it’s better to see an iPhone-shaped layout in the storyboard editor. Select the view controller and in the Attributes inspector, choose a Size of iPhone 4 inch. From the Object Library, drag a plain View object onto the canvas. Resize the view so that it is 160 by 284 points, and change its background color to be something other than white (for example, green):

SwiftAutoLayout14

There are two main reasons why you would drop a plain UIView onto a storyboard: a) You’re going to use it as a container for other views, which helps with organizing the content of your scenes; or b) It is a placeholder for a custom view or control, and you will also set its Class attribute to the name of your own UIView or UIControl subclass.

Let’s give this view some constraints. You’ve already seen two ways to make constraints: the Editor\Pin and Align menus, and Ctrl-dragging between views. There is a third method that you’ll use here. At the bottom of the Interface Builder window is a row of buttons:

Screen Shot 2014-09-26 at 7.53.49 PM

The four circled buttons are for Auto Layout. From left to right they are: Align, Pin, Resolve Auto Layout Issues, and Resizing Behavior. The first three perform the same functions as the corresponding items from the Editor menu. The Resizing Behavior button allows you to change what happens to the constraints when you resize views.

Select the green view and click the Pin button. A popup appears that lets you add a variety of constraints:

Autolayout Pin Menu

The Spacing to nearest neighbor section at the top is what you’ll use most often. Uncheck Constrain to margins then click the four T-bar thingies so they become solid red:

Pinning to superview with no margins

Constrain to margins is a new feature in Xcode 6 and iOS 8. Remember the blue guides that appear when you drag a view near to the edge of its superview? Now, instead of that creating a constraint of that distance from the very edge of the view, you can create a constraint of 0 distance from the view’s margin. A view can define its own margin sizes so this allows you to be more flexible about your layouts. For the purposes of this tutorial, we’ll stick to making constraints to the edges of the view.

This will create four new constraints between the green view and its superview, one for each side of the view. The actual spacing values may be different for you, depending on where you placed the view – you don’t have the change the values to match the ones above exactly. Click Add 4 Constraints to finish.

Your storyboard should now look something like this:

View pinned to its superviews edges

Maybe you wondered why the constraint at the top of the view doesn’t go all the way up to the top of the screen:

Swift ALU 19

Instead it stops at the status bar. But in iOS 7 the status bar is always drawn on top of the view controller — it is no longer a separate bar — so what gives? When you created the constraint it didn’t actually attach to the top of the screen but to an invisible line called the Top Layout Guide.

On a regular view controller this guide sits at 20 points from the top of the screen, at least when the status bar is not hidden. In a navigation controller it sits below the navigation bar. Because the navigation bar has a different height in landscape, the Top Layout Guide moves with the bar when the device is rotated. That makes it easy to place views relative to the navigation bar. There is also a Bottom Layout Guide that is used for the tab bar and toolbars.

This view needs four constraints to keep it in place. Unlike a button or label, a plain UIView does not have an intrinsic content size. There must always be enough constraints to determine the position and size of each view, so this view also needs constraints to tell it what size it needs to be.

You may wonder, where are these size constraints? In this case, the size of the view is implied by the size of the superview. The constraints in this layout are two Horizontal Spaces and two Vertical Spaces, and these all have fixed lengths. You can see this in the Document Outline:

Swift ALU 18

The width of the green view is calculated by the formula “width of superview minus (54 + 74)” and its height by the formula “height of superview minus (50 + 214)”. The space constraints are fixed, so the view has no choice but to resize. (Again, your values may be different depending on where you put the view.)

When you rotate the app, the dimensions of the superview change, so the formula changes with it.

You can see this for yourself when you run the app and flip to landscape, but you can also simulate it directly in Interface Builder. Click Editor > Simulated Screen > Pick an iPhone Size > Landscape. You will see what our app looks like in landscape now :

Screen Shot 2014-09-26 at 8.10.41 PM

You may not always want your UIView to resize when the device rotates, so you can use constraints to give the view a fixed width and/or height. Let’s do that now. Select the green view and click the Pin button; in the popup put checkmarks in front of Width and Height.

Using the pin menu to create size constraints

Click Add 2 Constraints to finish. You have now added two new constraints to the view, a 160 point Width constraint and a 284 point Height constraint:

Document outline showing new size constraints

Because Width and Height apply to just this view, they are located in the Document Outline under the View itself. Usually, constraints express a relationship between two different views – for example, the Horizontal and Vertical Space constraints are between the green view and its superview – but you can consider the Width and Height constraints to be a relationship between the green view and itself.

Run the app. Yup, looks good in portrait. Now flip over to landscape. Whoops! Not only does it not look like you wanted – the view has changed size again – but the Xcode debug pane has dumped a nasty error message that looks like this at the top:

Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. 
Try this: (1) look at each constraint and try to figure out which you don't expect; 
(2) find the code that added the unwanted constraint or constraints and fix it. 
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, 
refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    <NSLayoutConstraint:0x7f8deac8a910 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7f8deae26b50(667)]>"
)
 
Will attempt to recover by breaking constraint....

Remember when I said that there must be enough constraints so that Auto Layout can calculate the positions and sizes of all the views? Well, this is an example where there are too many constraints. Whenever you get the error “Unable to simultaneously satisfy constraints”, it means that your constraints are conflicting somewhere.

Let’s look at those constraints again:

Swift ALU 22

There are six constraints set on the green view, the four Spacing constraints you saw earlier (1-4) and the new Width and Height constraints that you have just set on it (5 and 6). So where is the conflict?

In portrait mode there shouldn’t be a problem because the math adds up. If you add the lengths of the Horizontal Space and Width constraints, then you should also end up at the width of the super view. Like wise for the vertical constraints.

But when you rotate the device to landscape, the math is off and Auto Layout doesn’t know what to do.

The conflict here is that either the width of the view is fixed and one of the margins must be flexible, or the margins are fixed and the width must be flexible. You can’t have both. So one of these constraints has to go. In the above example, you want the view to have the same width in both portrait and landscape, so the trailing Horizontal Space has got to go.

Remove the Horizontal Space at the right and the Vertical Space at the bottom. The storyboard should look like this:

Swift ALU 23

Now the view has just the right number of constraints to determine its size and position — no more, no less. Run the app and verify that the error message is gone and that the view stays the same size after rotating.

Even though Interface Builder does its best to warn you about invalid layouts, it cannot perform miracles. It will warn you when there are too few constraints but it doesn’t fare so well at detecting layouts with too many constraints.

At least at run time, Auto Layout spits out a detailed error message when something is wrong. You can learn more about analyzing these error messages and diagnosing layout problems in “Intermediate Auto Layout” in iOS 6 by Tutorials.

Painting the portraits

Drag a Label onto the green view. Notice that now the guides appear within that green view, because it will be the superview for the label.

Screen Shot 2014-09-26 at 8.35.14 PM

Position the label against the bottom margin, horizontally centered against the guides. Add a space constraint to anchor the label against the bottom of the green view, at 20 points distance. The quickest way is to use the Pin button and just select the T-bar at the bottom:

Swift ALU 24

Now add a constraint to center the label horizontally. You’ve seen how to do this with the Editor\Align menu but you can also use the Align button from the floating Auto Layout menu. Select the label and click the Align button to bring up the popup:

Swift ALU 25

Put a checkbox in front of Horizontal Center in Container and then click Add 1 Constraint. The storyboard should now look like this:

Swift ALU 26

Notice that these two new Horizontal and Vertical Space constraints are listed under the green view’s own Constraints section, not in the main view.

Drag a new Image View object onto the storyboard, and make the layout look like this:

Image view with constraints

The image view is pinned to the top, left, and right edges of its superview, but its bottom is connected to the top of the label with a standard spacing of 8 points. If you’re unsure of how to do this, then follow these steps.

1. Drag the image view into the green view but don’t worry too much about its size or position:

Swift ALU 27

2. With the image view selected, press the Pin button and choose the following options:

Swift ALU 28

The top, left, and right T-bars are set to 20 points but the bottom one is set to 8 points. Important: For Update Frames you should choose Items of New Constraints. If you had left this to the default of None, the storyboard would look something like this:

Swift ALU 29

The constraints you chose result in a different frame than the image view’s current position and size. But if you choose Items of New Constraints, Interface Builder will automatically adjust the frame as it adds the constraints and everything looks dandy:

Swift ALU 30

Of course, if you do end up with a misplaced frame, you can use the Resolve Auto Layout Issues button to fix it:

Swift ALU 31

Adding Images

Next, download the resources for this tutorial and unzip the file. You will find an Images folder – add this folder into your project. Set Ray.png as the image for the image view, change the image view’s mode to Aspect Fit and set its background color to white. Change the label’s text to say “Ray”.

Your layout should now look like this:

Swift ALU 32

Notice that the constraints inside the green view turned to orange. This happened the moment you set the image on the image view. How come your layout is suddenly invalid? Fortunately you can take the guesswork out of it and let Xcode tell you exactly what’s wrong.

Click the small red arrow next to View Controller Scene in the Document Outline to view the issues:

Swift ALU 33

You have a Content Priority Ambiguity error. That’s quite the mouthful. This is what it means: If neither the image view nor the label has a fixed height, then Auto Layout doesn’t know by how much to scale each if the height of the green view should change. (Interface Builder seems to ignore for now that the green view actually has a fixed Height constraint set on it.)

Let’s say at some point in your app the green view becomes 100 points taller. How should Auto Layout distribute these new 100 points among the label and the image view? Does the image view become 100 points taller while the label stays the same size? Or does the label become taller while the image view stays the same? Do they both get 50 points extra, or is it split 25/75, 40/60, or in some other possible combination?

If you don’t solve this problem somehow then Auto Layout is going to have to guess and the results may be unpredictable.

The proper solution is to change the Content Compression Resistance Priority of the label. As the name suggests, this value determines how resistant a view is to being compressed, or shrunk. A higher value here means the view will be less likely to be compressed and more likely to stay the same.

Similarly, the Content Hugging Priority determines how resistant a view is to being expanded. You can imagine “hugging” here to mean “size to fit” – the bounds of the view will “hug” or be close to the intrinsic content size. A higher value here means the view will be less likely to grow and more likely to stay the same.

Go into the Size inspector for the label and set the vertical Content Compression Resistance Priority to 751. That makes it one higher than the priority of the image view. While you’re at it, set Content Hugging Priority to 252. When the superview changes size, that means the image view will be the one to resize, and the label will stay pretty much the same size.

Swift ALU 34

The T-bars should turn blue again and the Auto Layout warnings are gone.

Adding the other heads

Drag the green view into the main view’s top-left corner. Recall that the green view had Horizontal Space and Vertical Space constraints that determined its position in the parent view. It still has those and they cause the frame of the view to be misaligned.

Swift ALU 35

To fix this, use the Resolve Auto Layout Issues button and choose Update Constraints. Previously you used Update Frames, which moved and resized the view the match the constraints. Here you want to do the opposite: you want the constraints to update to match the frame.

Note that the Vertical Space at the top is now negative. That happens because this constraint is connected to the Top Layout Guide. But there’s no reason why constraints cannot have negative values, so you can leave this as is. (If it bothers you, delete that “Vertical Space (-20)” constraint and pin the view to the top of the window.)

The Horizontal Space now has size 0 and is represented by a thick blue line at the left edge of the window. So even though the view sits completely in the corner, it still needs constraints to anchor it there:

Swift ALU 36

Select the green view and tap ⌘D to duplicate it. Move the duplicate into the top-right corner:

Swift ALU 37

Notice that the T-bars are orange. When you made the duplicate, it apparently lost its constraints for the X and Y position. To fix that, pin the view to the top and the right edges of the window.

Duplicate two more times and put these copies in the bottom-left and bottom-right corners, respectively. Again, pin these views to their corners.

Change the screen design to the following:

Swift ALU 38

Those are some good-looking programmers! :-)

Run the app. It looks good in portrait, but not so much in landscape:

Swift ALU 39

It should be pretty obvious what went wrong: you’ve set a fixed width and height on the four brightly-colored container views, so they will always have those sizes, regardless of the size of their superview.

Select the fixed Width and fixed Height constraints from all four views and delete them (this is easiest in the Document Outline). If you run the app now, you’ll get something like this:

Swift ALU 40

Note: If you’re wondering why some of the views are larger than others, this is again related to the intrinsic content size. The size of the image determines how large the image view is; the size of the text determines how large the label is. Taken together with the constraints for the margins — 20 points on all sides — this determines the total size of each view.

This looks very much like the problem you solved in the introduction in part 1, so if you think back to how you solved that, you’ll recall that you gave the views equal widths and heights.

Select all four colored views. This is easiest in the Document Outline; hold and click on the four views. You can add the constraints in one go. In the Pin popup put checkmarks in front of Equal Widths and Equal Heights and then press Add 6 Constraints.

Swift ALU 41

Run the app again and rotate the device. Hmm… still no good:

Swift ALU 42

All the views do have the same height, and they also appear to have the same width, so your constraints are being met. It’s just not the width and height that you want them to have.

Just saying that all four views must have equal sizes is not enough to determine what those sizes should actually be, because Auto Layout does not know how these four views are connected to each other. They appear side-by-side in the design, but there are no actual constraints between them. Auto Layout does not know that it needs to split the window width between the “Ray” and “Matthijs” boxes.

If Auto Layout can’t figure this out by itself, you have to tell it.

To be related

Select the Ray and Matthijs boxes and choose Pin\Horizontal Spacing from the Editor menu. Because the boxes are side-by-side, this adds a Horizontal Space constraint with size 0 between them, and that is enough to let Auto Layout know how these two views are related. Also put a Vertical Space between the Ray and Dennis Ritchie boxes using Editor\Pin\Vertical Spacing.

Run the app again, and this time it looks all right:

Gallery app laid out correctly in landscape

Where To Go From Here?

If you’ve made it this far, congratulations – you now know what Auto Layout is all about, and have experimented with the basics! But there’s a lot left to learn…

The tutorial you have just read is only the first half of the Beginning Auto Layout chapter from the book iOS 6 by Tutorials. The second half teaches how to use Auto Layout to create more “real-world” screen layouts, and everything else you need to know about using Auto Layout from Interface Builder. You can also check out our free video tutorial on Beginning Auto Layout.

To continue on with more advanced topics, check out our book iOS 8 by Tutorials which has a whole section covering size classes and adaptive layout.

If you have any questions or comments as you continue on you auto layout journey, please join the forum discussion below!

Beginning Auto Layout Tutorial in Swift: Part 2/2 is a post from: Ray Wenderlich

The post Beginning Auto Layout Tutorial in Swift: Part 2/2 appeared first on Ray Wenderlich.

iOS Games by Tutorials Second Edition: Print Version Now Available!

$
0
0
iOS Games by Tutorials Second Edition print version now available!

iOS Games by Tutorials Second Edition print version now available!

Good news – the print version of our popular book iOS Games by Tutorials Second Edition is now available!

This is the print version for the brand new second edition, which is fully updated for iOS 8 and Swift.

Since the book was previously in Objective-C, this is a massive update – an entirely new programming language!

In addition, we also updated all games in the book to be Universal apps that work on both iPhone and iPad (previously they were iPhone only). We also added some extra chapters that show how to port the games to the Mac.

Keep reading for some more pictures of the new second edition, learn about a discount for iOS Games by Tutorials PDF customers, and enter for (another) print book giveaway!

The Book

As you know, iOS Games by Tutorials is huge – over 800 pages! In fact it was so large that we had to make a few chapters downloadable so it would fit :]

Here’s a picture of what the book looks like next to my iPhone 5:

Side view of iOS Games by Tutorials Second Edition book.

The print quality came out great, and is easy to read and thumb through. Here’s what the inside of the book looks like:

Inside view of iOS Games by Tutorials Second Edition book.

Note that on our store page, we have two options:

  • Just the PDF. Nothing’s changed here – if you prefer electronic books you can buy just the PDF same way you usually would.
  • The PDF + Print Version Bundle. This gives you the best of both worlds. It also saves you a lot of money – you’re effectively getting the printed books at a big discount compared to buying them separately.

Note we don’t offer an option for just the printed book. This is because we like to keep our books as up-to-date as we can, and we want to be able to give all our customers access to these updates (in PDF form). For example, iOS Apprentice PDF customers have received 4 major updates since it was first released (from iOS 4->5->6->7->8)!

Discount for iOS Games by Bundle PDF Customers – 1 Week Only

If you bought the PDF version of iOS Games by Tutorials Bundle, you are eligible for a special discount on the print version, to effectively “upgrade” your order to the PDF+Print bundle.

I will send all PDF version customers an email with instructions for how to upgrade your purchase in a few minutes.

If for some reason you do not get the email, just contact me and we’ll get it sorted.

Note this discount will expire in just 1 week, so if you’re interested snag it fast! :]

Back cover of iOS Games by Tutorials Second Edition.

The Giveaway

One last thing. To celebrate the launch, we’re giving away a free copy of the book!

All you have to do to enter is leave a comment on this post – this Friday we’ll choose 1 lucky winner to get the free copy.

And yes – if you entered the Swift by Tutorials Bundle giveaway, you can enter this one too :]

That’s it – we hope you all enjoy the print version of iOS Games by Tutorials Second Edition! If you’d like to pick up a copy just check out our store page.

We hope you enjoy the new print version! :]

iOS Games by Tutorials Second Edition: Print Version Now Available! is a post from: Ray Wenderlich

The post iOS Games by Tutorials Second Edition: Print Version Now Available! appeared first on Ray Wenderlich.


Video Tutorial: WatchKit Part 16: Communicating with the iOS App

Video Tutorial: WatchKit Part 17: Notifications

Custom Control Tutorial for iOS and Swift: A Reusable Knob

$
0
0

Update note: This tutorial was updated for iOS 8 and Swift by Mikael Konutgan. Original post by Tutorial Team member Sam Davies.

Custom UI controls are extremely useful when you need some new functionality in your app — especially when they’re generic enough to be reusable in other apps.

We have an excellent tutorial providing an introduction to custom UI Controls in Swift. That tutorial walks you through the creation of a custom double-ended UISlider that lets you select a range with start and end values.

This custom control tutorial takes that concept a bit further and covers the creation of a control kind of like a circular slider inspired by a control knob, such as those found on a mixer:

sound_desk_knob

UIKit provides the UISlider control, which lets you set a floating point value within a specified range. If you’ve used any iOS device, then you’ve probably used a UISlider to set volume, brightness, or any one of a multitude of other variables. The project you’ll build today will have exactly the same functionality, but in a circular form.

Prepare yourself for bending straight lines into bezier curves, and let’s get started!

Getting Started

First, download the starter project here. This is a simple single view application. The storyboard has a few controls that are wired up to the main view controller. You’ll use these controls later in the tutorial to demonstrate the different features of the knob control.

Build and run your project just to get a sense of how everything looks before you dive into the coding portion; it should look like the screenshot below:

starter-screenshot

To create the class for the knob control, click File/New/File… and select iOS/Source/Cocoa Touch Class. On the next screen, specify the class name as Knob, have the class inherit from UIControl and make sure the language is Swift. Click Next, choose the “KnobDemo” directory and click Create.

Before you can write any code for the new control, you must first add it to the view controller so you can see how it evolves visually.

Open up ViewController.swift and add the following property just after the other ones:

var knob: Knob!

Now replace viewDidLoad with the following code:

override func viewDidLoad() {
  super.viewDidLoad()
 
  knob = Knob(frame: knobPlaceholder.bounds)
  knobPlaceholder.addSubview(knob)
}

This creates the knob and adds it to the placeholder in the storyboard. The knobPlaceholder property is already wired up as an IBOutlet.

Open Knob.swift and replace the boiler-plate class definition with the following code:

public class Knob: UIControl {
  public override init(frame: CGRect) {
    super.init(frame: frame)
 
    backgroundColor = tintColor
  }
 
  public required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
}

This code makes the class public, defines the two initializers and sets the background color of the knob so that you can see it on the screen. It is required for a view that defines init(frame:) to also define init(coder:), but you will not deal with that method now, so you just raise an error. This is the exact code Xcode suggests if you don’t define init(coder:).

Build and run your app and you’ll see the following:

buildandrun1

Okay, you have the basic building blocks in place for your app. Time to work on the API for your control!

Designing Your Control’s API

Your main reason for creating a custom UI control is to create a handy and reusable component. It’s worth taking a bit of time up-front to plan a good API for your control; developers using your component should understand how to use it from looking at the API alone, without any need to open up the source code. This means that you’ll need to document your API as well!

The public functions and properties of your custom control is its API.

In Knob.swift, add the following code to the Knob class:

private var backingValue: Float = 0.0
 
/** Contains the receiver’s current value. */
public var value: Float {
  get { return backingValue }
  set { setValue(newValue, animated: false) }
}
 
/** Sets the receiver’s current value, allowing you to animate the change visually. */
public func setValue(value: Float, animated: Bool) {
  if value != backingValue {
    backingValue = min(maximumValue, max(minimumValue, value))
  }
}
 
/** Contains the minimum value of the receiver. */
public var minimumValue: Float = 0.0
 
/** Contains the maximum value of the receiver. */
public var maximumValue: Float = 1.0
 
/** Contains a Boolean value indicating whether changes
    in the sliders value generate continuous update events. */
public var continuous = true
  • value, minimumValue and maximumValue simply set the basic operating parameters of your control.
  • setValue(_:animated:) lets you set the value of the control programmatically, while the additional boolean parameter indicates whether or not the change in value is to be animated. You want to use mostly the same code when you set the value or call setValue(_:animated:). To achieve this, you use a private backingValue property that holds the actual value. value then just returns that backing value when you get it and calls setValue(_:animated:) with false when you set it. Finally you ensure that the value is bounded within the limits associated with the control before actually setting it.
  • If continuous is set to true, then the control calls back repeatedly as the value changes; if it is set to false, the the control only calls back once after the user has finished interacting with the control.

You’ll ensure that these properties behave appropriately as you fill out the knob control implementation later on in this tutorial.

Those comments might seem superfluous, but Xcode can pick them up and show them in a tooltip, like so:

doc_tooltip

Code-completion tips like this are a huge time-saver for the developers who use your control, whether that’s you, your teammates or other people!

Now that you’ve defined the API of your control, it’s time to get cracking on the visual design.

Setting the Appearance of Your Control

Our previous tutorial compares Core Graphics and images as two potential methods to set the appearance of your custom control. However, that’s not an exhaustive list; in this custom UI tutorial, you’ll explore a third option to control the visuals of your control: Core Animation layers.

Whenever you use a UIView, it’s backed by a CALayer, which helps iOS optimize the rendering on the GPU. CALayer objects manage visual content and are designed to be incredibly efficient for all types of animations.

Your knob control will be made up of two CALayer objects: one for the track, and one for the pointer itself. This will result in extremely good performance for your animation, as you’ll see later.

The diagram below illustrates the basic construction of your knob control:

CALayerDiagram

The blue and red squares represent the two CALayer objects; the blue layer contains the track of the knob control, and the red layer the pointer. When overlaid, the two layers create the desired appearance of a moving knob. The difference in coloring above is only to illustrate the different layers of the control — not to worry, your control will look much nicer than that. ;]

The reason to use two separate layers becomes obvious when the pointer moves to represent a new value. All you need to do is rotate the layer containing the pointer, which is represented by the red layer in the diagram above.

It’s cheap and easy to rotate layers in Core Animation. If you chose to implement this using Core Graphics and override drawRect, the entire knob control would be re-rendered in every step of the animation. This is a very expensive operation, and will likely result in sluggish animation, particularly if changes to the control’s value invoke other re-calculations within your app.

To keep the appearance parts separate from the control parts, add a new private class to the end of Knob.swift:

private class KnobRenderer {
}

This class will keep track of the code associated with rendering the knob itself. That will add a clean separation between the public Knob class and its internal workings.

Next, add the following code inside the KnobRenderer definition:

var strokeColor: UIColor {
  get {
    return UIColor(CGColor: trackLayer.strokeColor)
  }
 
  set(strokeColor) {
    trackLayer.strokeColor = strokeColor.CGColor
    pointerLayer.strokeColor = strokeColor.CGColor
  }
}
 
var lineWidth: CGFloat = 1.0
 
let trackLayer = CAShapeLayer()
var startAngle: CGFloat = 0.0
var endAngle: CGFloat = 0.0
 
let pointerLayer = CAShapeLayer()
 
var backingPointerAngle: CGFloat = 0.0
 
var pointerAngle: CGFloat {
  get { return backingPointerAngle }
  set { setPointerAngle(newValue, animated: false) }
}
 
func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
  self.backingPointerAngle = pointerAngle
}
 
var pointerLength: CGFloat = 0.0

Most of these properties deal with the visual appearance of the knob, with two CAShapeLayer properties representing the two layers which make up the overall appearance of the control. The strokeColor property just delegates to the strokeColor of the two layers and you’re using the same pattern as above with the backingPointerAngle, pointerAngle properties along with the setPointerAngle(_:animated:) method.

Also add an initializer to the class:

init() {
  trackLayer.fillColor = UIColor.clearColor().CGColor
  pointerLayer.fillColor = UIColor.clearColor().CGColor
}

This sets the appearance of the two layers as transparent.

You’ll create the two shapes that make up the overall knob as CAShapeLayer objects. These are a special subclasses of CALayer that draw a bezier path using anti-aliasing and some optimized rasterization. This makes CAShapeLayer an extremely efficient way to draw arbitrary shapes.

Add the following two methods to the KnobRenderer class:

func updateTrackLayerPath() {
  let arcCenter = CGPoint(x: trackLayer.bounds.width / 2.0, y: trackLayer.bounds.height / 2.0)
  let offset = max(pointerLength, trackLayer.lineWidth / 2.0)
  let radius = min(trackLayer.bounds.height, trackLayer.bounds.width) / 2.0 - offset;
  trackLayer.path = UIBezierPath(arcCenter: arcCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true).CGPath
}
 
func updatePointerLayerPath() {
  let path = UIBezierPath()
  path.moveToPoint(CGPoint(x: pointerLayer.bounds.width - pointerLength - pointerLayer.lineWidth / 2.0, y: pointerLayer.bounds.height / 2.0))
  path.addLineToPoint(CGPoint(x: pointerLayer.bounds.width, y: pointerLayer.bounds.height / 2.0))
  pointerLayer.path = path.CGPath
}

updateTrackLayerPath creates an arc between the startAngle and endAngle values with a radius that ensures the pointer will fit within the layer, and positions it on the center of the trackLayer. Once you create the UIBezierPath, you then use the CGPath property to set the path on the appropriate CAShapeLayer.

CGPathRef is the Core Graphics equivalent of UIBezierPath. Since UIBezierPath has a nicer, more modern API, you use that to initially create the path, and then convert it to a CGPathRef.

updatePointerLayerPath creates the path for the pointer at the position where angle is equal to zero. Again, you create a UIBezierPath, convert it to a CGPathRef and assign it to the path property of your CAShapeLayer. Since the pointer is a simple straight line, all you need to draw the pointer are moveToPoint and addLineToPoint.

Calling these methods redraws the two layers; this must happen when any of the properties used by these methods are modified. To do that, you’ll need to implement property observers for the properties you added to the API for the renderer to use.

First create a single update method:

func update() {
  trackLayer.lineWidth = lineWidth
  pointerLayer.lineWidth = lineWidth
 
  updateTrackLayerPath()
  updatePointerLayerPath()
}

You’ll call this method to update both layers and their line widths.

Now change the lineWidth, startAngle, endAngle, and pointerLength properties of KnobRenderer as follows:

var lineWidth: CGFloat = 1.0 {
  didSet { update() }
}
 
var startAngle: CGFloat = 0.0 {
  didSet { update() }
}
 
var endAngle: CGFloat = 0.0 {
  didSet { update() }
}
 
var pointerLength: CGFloat = 0.0 {
didSet { update() }
}

You may have noticed that the two methods for updating the shape layer paths rely on one more property which has never been set — namely, the bounds of each of the shape layers. Since you never set the CAShapeLayer bounds, they currently have a zero-sized bounds.

Add a new method to KnobRenderer:

func update(bounds: CGRect) {
  let position = CGPoint(x: bounds.width / 2.0, y: bounds.height / 2.0)
 
  trackLayer.bounds = bounds
  trackLayer.position = position
 
  pointerLayer.bounds = bounds
  pointerLayer.position = position
 
  update()
}

The above method takes a bounds rectangle, resizes the layers to match and positions the layers in the center of the bounding rectangle. As you’ve changed a property that affects the paths, you must call the update() method manually.

Although the renderer isn’t quite complete, there’s enough here to demonstrate the progress of your control. Add a property to hold an instance of your renderer to the Knob class:

private let knobRenderer = KnobRenderer()

Then add the following method, also to Knob:

func createSublayers() {
  knobRenderer.update(bounds)
  knobRenderer.strokeColor = tintColor
  knobRenderer.startAngle = -CGFloat(M_PI * 11.0 / 8.0);
  knobRenderer.endAngle = CGFloat(M_PI * 3.0 / 8.0);
  knobRenderer.pointerAngle = knobRenderer.startAngle;
  knobRenderer.lineWidth = 2.0
  knobRenderer.pointerLength = 6.0
 
  layer.addSublayer(knobRenderer.trackLayer)
  layer.addSublayer(knobRenderer.pointerLayer)
}

The above method sets the knob renderer’s size, then adds the two layers as sublayers of the control’s layer. You’ve temporarily hard-coded the startAngle and endAngle properties just so that your view will render for testing.

An empty view would be taking the iOS 7/8 design philosophy a step too far, so you need to make sure createSublayers is called when the knob control is being constructed. Add the following line to the initializer:

createSublayers()

You can also remove the following line from the initializer since it is no longer required:

backgroundColor = tintColor

Your initializer should now look like this:

public override init(frame: CGRect) {
    super.init(frame: frame)
 
    createSublayers()
}

Build and run your app, and your control should look like the one below:

buildandrun2

It’s not yet complete, but you can see the basic framework of your control taking shape.

Exposing Appearance Properties in the API

Currently, the developer using your control has no way of changing the control’s appearance, since all of the properties which govern the look of the control are hidden away in the private renderer.

To fix this, add the following properties to the Knob class:

/** Specifies the angle of the start of the knob control track. Defaults to -11π/8 */
public var startAngle: CGFloat {
  get { return knobRenderer.startAngle }
  set { knobRenderer.startAngle = newValue }
}
 
/** Specifies the end angle of the knob control track. Defaults to 3π/8 */
public var endAngle: CGFloat {
  get { return knobRenderer.endAngle }
  set { knobRenderer.endAngle = newValue }
}
 
/** Specifies the width in points of the knob control track. Defaults to 2.0 */
public var lineWidth: CGFloat {
  get { return knobRenderer.lineWidth }
  set { knobRenderer.lineWidth = newValue }
}
 
/** Specifies the length in points of the pointer on the knob. Defaults to 6.0 */
public var pointerLength: CGFloat {
  get { return knobRenderer.pointerLength }
  set { knobRenderer.pointerLength = newValue }
}

Just as before, there are plenty of comments to assist developers when they use the control.

The four properties are fairly straightforward and simply proxy for the properties in the renderer. Since the control itself doesn’t actually need backing variables for these properties, it can rely on the renderer to store the values instead.

To test that the new API bits are working as expected, add the following code to the end of viewDidLoad in ViewController.swift:

knob.lineWidth = 4.0
knob.pointerLength = 12.0

Build and run your project again; you’ll see that the line thickness and the length of the pointer have both increased, as shown below:

buildandrun4

Changing Your Control’s Color

You may have noticed that you didn’t create any color properties on the public API of the control — and for good reason. There is a property on UIView, tintColor that you will use. In fact, you’re already using it to set the color of the knob in the first place — check the knobRenderer.strokeColor = tintColor line in createSublayers if you don’t believe me. :]

So you might expect that adding the following line to the end of viewDidLoad inside ViewController.swift will change the color of the control:

view.tintColor = UIColor.redColor()

Add the code above and build and run your project, you’ll quickly be disappointed. However, the UIButton has updated appropriately, as demonstrated below:

pre-tint-color

Although you’re setting the renderer’s color when the UI is created, it won’t be updated when the tintColor changes. Luckily, this is really easy to fix.

Add the following function to the Knob class:

public override func tintColorDidChange() {
  knobRenderer.strokeColor = tintColor
}

Whenever you change the tintColor property of a view, UIKit calls tintColorDidChange on all views beneath the current view in the view hierarchy that haven’t had their tintColor property set manually. So to listen for tintColor updates anywhere above the view in the current hierarchy, all you have to do is implement tintColorDidChange in your code and update the view’s appearance appropriately.

Build and run your project; you’ll see that the red tint has been picked up by your control as shown below:

buildandrun5

Setting the Control’s Value Programmatically

Although your knob looks pretty nice, it doesn’t actually do anything. In this next phase you’ll modify the control to respond to programmatic interactions — that is, when the value property of the control changes.

At the moment, the value of the control is saved when the value property is modified directly or when you call setValue(_:animated:). However, there isn’t any communication with the renderer, and the control won’t re-render.

The renderer has no concept of value; it deals entirely in angles. You’ll need to update setValue(_:animated:) in Knob so that it converts the value to an angle and passes it to the renderer.

Go the the Knob class and update setValue(_:animated:) so that it matches the following code:

public func setValue(value: Float, animated: Bool) {
  if value != self.value {
    // Save the value to the backing value
    // Make sure we limit it to the requested bounds
    self.backingValue = min(self.maximumValue, max(self.minimumValue, value))
 
    // Now let's update the knob with the correct angle
    let angleRange = endAngle - startAngle
    let valueRange = CGFloat(maximumValue - minimumValue)
    let angle = CGFloat(value - minimumValue) / valueRange * angleRange + startAngle
    knobRenderer.setPointerAngle(angle, animated: animated)
  }
}

The code above now works out the appropriate angle for the given value by mapping the minimum and maximum value range to the minimum and maximum angle range and sets the pointerAngle property on the renderer.

Note you’re just passing the value of animated to the renderer, but nothing is actually animating at the moment — you’ll fix this in the next section.

Although the pointerAngle property is being updated, it doesn’t yet have any effect on your control. When the pointer angle is set, the layer containing the pointer should rotate to the specified angle to give the impression that the pointer has moved.

Update setPointerAngle(_:animated:) to the following:

func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
  pointerLayer.transform = CATransform3DMakeRotation(pointerAngle, 0.0, 0.0, 0.1)
 
  self.backingPointerAngle = pointerAngle
}

This simply creates a rotation transform which rotates the layer around the z-axis by the specified angle.

The transform property of CALayer expects to be passed a CATransform3D, not a CGAffineTransform like UIView. This means that you can perform transformations in three dimensions.

CGAffineTransform uses a 3×3 matrix and CATransform3D uses a 4×4 matrix; the addition of the z-axis requires the extra values. At their core, 3D transformations are simply matrix multiplications. You can read more about matrix multiplication in this Wikipedia article.

To demonstrate that your transforms work, you’re going to link the UISlider present in the starter project with the knob control in the view controller. As you adjust the slider, the value of the knob will change appropriately.

The UISlider has already been linked to sliderValueChanged so update that method to the following implementation:

@IBAction func sliderValueChanged(slider: UISlider) {
  knob.value = slider.value
}

The method will simply update the knob value with the new slider value.

Build and run your project; change the value of the UISlider and you’ll see the pointer on the knob control move to match as shown below:

buildandrun6

There’s a little bonus here — your control is animating, despite the fact that you haven’t started coding any of the animations yet! What gives?

Core Animation is quietly calling implicit animations on your behalf. When you change certain properties of CALayer — including transform — the layer animates smoothly from the current value to the new value. Remember the CA in CALayer stands for Core Animation!

Usually this functionality is really cool; you get nice looking animations without doing any work. However, you want a little more control, so you’ll animate things yourself.

Update setPointerAngle as follows:

func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
  CATransaction.begin()
  CATransaction.setDisableActions(true)
 
  pointerLayer.transform = CATransform3DMakeRotation(pointerAngle, 0.0, 0.0, 0.1)
 
  CATransaction.commit()
 
  self.backingPointerAngle = pointerAngle
}

To prevent these implicit animations, you wrap the property change in a CATransaction and disable animations for that interaction.

Build and run your app once more; you’ll see that as you move the UISlider, the knob follows instantaneously.

Animating Changes to the Control’s Value

At the moment, setting the animated parameter to true has no effect on your control. To enable this bit of functionality, update setPointerAngle once again as follows:

func setPointerAngle(pointerAngle: CGFloat, animated: Bool) {
  CATransaction.begin()
  CATransaction.setDisableActions(true)
 
  pointerLayer.transform = CATransform3DMakeRotation(pointerAngle, 0.0, 0.0, 0.1)
 
  if animated {
    let midAngle = (max(pointerAngle, self.pointerAngle) - min(pointerAngle, self.pointerAngle) ) / 2.0 + min(pointerAngle, self.pointerAngle)
    let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
    animation.duration = 0.25
 
    animation.values = [self.pointerAngle, midAngle, pointerAngle]
    animation.keyTimes = [0.0, 0.5, 1.0]
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    pointerLayer.addAnimation(animation, forKey: nil)
  }
 
  CATransaction.commit()
 
  self.backingPointerAngle = pointerAngle
}

The difference here is when animated is set; if you had left this section with its implicit animation, the direction of rotation would be chosen to minimize the distance travelled. This means that animating between 0.98 and 0.1 wouldn’t rotate your layer counter-clockwise, but instead rotate clockwise over the end of the track, and into the bottom, which is not what you want!

In order to specify the rotation direction, you need to use a keyframe animation. That’s simply an animation where you specify some in-between points in addition to the usual start and end points.

Core Animation supports keyframe animations; in the above method, you’ve created a new CAKeyFrameAnimation and specified that the property to animate is the rotation around the z-axis with transform.rotation.z as its keypath.

Next, you specify three angles through which the layer should rotate: the start point, the mid-point and finally the end point. Along with that, there’s an array specifying the normalized times (as percentages) at which to reach those values. Adding the animation to the layer ensures that once the transaction is committed the animation will start.

In order to see this new functionality in action, you can use the “Random Value” button which is part of the app’s main view controller. This button causes the slider and knob controls to move to a random value, and uses the current setting of the animate switch to determine whether or not the change to the new value should be instantaneous or animated.

Update randomButtonTouched in ViewController to match the following:

@IBAction func randomButtonTouched(button: UIButton) {
  let randomValue = Float(arc4random_uniform(101)) / 100.0
  knob.setValue(randomValue, animated: animateSwitch.on)
  valueSlider.setValue(randomValue, animated: animateSwitch.on)
}

The above method generates a random value between 0.00 and 1.00 and sets the value on both controls. It then inspects the on property of animateSwitch to determine whether or not to animate the transition to the new value.

Build and run your app; tap the Random Value button a few times with the animate switch toggled on, then tap the Random Value button a few times with the animate switch toggled off to see the difference the animated parameter makes.

Updating the Label

Open ViewController.swift and add a method to update the label:

func updateLabel() {
  valueLabel.text = NSNumberFormatter.localizedStringFromNumber(knob.value, numberStyle: .DecimalStyle)
}

This will show the current value selected by the knob control. Next, add a call to this new method at the end of both sliderValueChanged and randomButtonTouched like this:

updateLabel()

Finally, update the initial value of the knob and the label to be the initial value of the slider so that all they are in sync when the app starts. Add the following code to the end of viewDidLoad:

knob.value = valueSlider.value
updateLabel()

Build and run, and run a few tests to make sure the label shows the correct value.

buildandrun5

Responding to Touch Interaction

The knob control you’ve built responds extremely well to programmatic interaction, but that alone isn’t terribly useful for a UI control. In this final section you’ll see how to add touch interaction using a custom gesture recognizer.

As you touch the screen, iOS delivers a series of UITouch events to the appropriate objects. When a touch occurs inside of a view with one or more gesture recognizers attached, the touch event is delivered to the gesture recognizers for interpretation. Gesture recognizers determine whether a given sequence of touch events matches a specific pattern; if so, they send an action message to a specified target.

Apple provides a set of pre-defined gesture recognizers, such as tap, pan and pinch. However, there’s nothing to handle the single-finger rotation you need for the knob control. Looks like it’s up to you to create your own custom gesture recognizer.

Add a new private class to the end of Knob.swift:

import UIKit.UIGestureRecognizerSubclass
 
private class RotationGestureRecognizer: UIPanGestureRecognizer {
}

This custom gesture recognizer will behave like a pan gesture recognizer; it will track a single finger dragging across the screen and update the location as required. For this reason, it subclasses UIPanGestureRecognizer.

You’ll also need the import statement so you can override some gesture recognizer methods later.

Note: You might be wondering why you’re adding all these private classes to Knob.swift rather than the usual one-class-per-file. For this project, it makes it easy to distribute just a single file to anyone who wants to use the control. You don’t have to worry about forgetting to include all your supplementary helper classes or leaving something behind.

Add the following new property to your new RotationGestureRecognizer class:

var rotation: CGFloat = 0.0

rotation represents the touch angle of the line which joins the current touch point to the center of the view to which the gesture recognizer is attached, as demonstrated in the following diagram:

GestureRecogniserDiagram

There are three methods of interest when subclassing UIGestureRecognizer: they represent the time that the touches begin, the time they move and the time they end. You’re only interested when the gesture starts and when the user’s finger moves on the screen.

Add the following two methods to RotationGestureRecognizer:

override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
  super.touchesBegan(touches, withEvent: event)
 
  updateRotationWithTouches(touches)
}
 
override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!) {
  super.touchesMoved(touches, withEvent: event)
 
  updateRotationWithTouches(touches)
}

Both of these methods call through to their super equivalent, and then call a utility function which you’ll add next:

func updateRotationWithTouches(touches: NSSet!) {
  let touch = touches.anyObject() as UITouch
  let location = touch.locationInView(self.view)
  self.rotation = rotationForLocation(location)
}
 
func rotationForLocation(location: CGPoint) -> CGFloat {
  let offset = CGPoint(x: location.x - view!.bounds.midX, y: location.y - view!.bounds.midY)
  return atan2(offset.y, offset.x)
}

updateRotationWithTouches takes the NSSet of touches and extracts one using anyObject. It then uses locationInView to translate the touch point into the coordinate system of the view associated with this gesture recognizer. It then updates the rotation property using rotationForLocation, which uses some simple geometry to find find the angle as demonstrated below:

AngleCalculation

x and y represent the horizontal and vertical positions of the touch point within the control. The tangent of the rotation, that is the touch angle is equal to h / w, so to calculate rotation all you need to do is establish the following lengths:

  • h = y - (view height) / 2.0 (since the angle should increase in a clockwise direction)
  • w = x - (view width) / 2.0

rotationForLocation: performs this calculation for you, and returns the angle required.

Finally, your gesture recognizer should only work with one touch at a time. Add the following initializer override to the class:

override init(target: AnyObject, action: Selector) {
  super.init(target: target, action: action)
 
  minimumNumberOfTouches = 1
  maximumNumberOfTouches = 1
}

This initializer limits the number of recognized touches to just one.

Wiring Up the Custom Gesture Recognizer

Now that you’ve completed the custom gesture recognizer, you just need to wire it up to the knob control.

Update init(frame:) of Knob to the following:

public override init(frame: CGRect) {
  super.init(frame: frame)
 
  createSublayers()
 
  let gr = RotationGestureRecognizer(target: self, action: "handleRotation:")
  self.addGestureRecognizer(gr)
}

The two lines added create a gesture recognizer in the familiar way: simply create a recognizer, specify where it should call back when activated then add it to the view.

Next, add the following method to Knob:

func handleRotation(sender: AnyObject) {
  let gr = sender as RotationGestureRecognizer
 
  // 1. Mid-point angle
  let midPointAngle = (2.0 * CGFloat(M_PI) + self.startAngle - self.endAngle) / 2.0 + self.endAngle
 
  // 2. Ensure the angle is within a suitable range
  var boundedAngle = gr.rotation
  if boundedAngle > midPointAngle {
      boundedAngle -= 2.0 * CGFloat(M_PI)
  } else if boundedAngle < (midPointAngle - 2.0 * CGFloat(M_PI)) {
      boundedAngle += 2 * CGFloat(M_PI)
  }
 
  // 3. Bound the angle to within the suitable range
  boundedAngle = min(self.endAngle, max(self.startAngle, boundedAngle))
 
  // 4. Convert the angle to a value
  let angleRange = endAngle - startAngle
  let valueRange = maximumValue - minimumValue
  let valueForAngle = Float(boundedAngle - startAngle) / Float(angleRange) * valueRange + minimumValue
 
  // 5. Set the control to this value
  self.value = valueForAngle
}

This method looks quite long and complicated, but the concept is pretty simple – it simply extracts the angle from the custom gesture recognizer, converts it to the value represented by this angle on the knob control, and then sets the value to trigger the UI updates.

Going through the commented sections of the code above, you’ll find the following:

  1. First, you calculate the angle which represents the ‘mid-point’ between the start and end angles. This is the angle which is not part of the knob track, and instead represents the angle at which the pointer should flip between the maximum and minimum values.
  2. The angle calculated by the gesture recognizer will be between and π, since it uses the inverse tangent function. However, the angle required for the track should be continuous between the startAngle and the endAngle. Therefore, create a new boundedAngle variable and adjust it to ensure that it remains within the allowed ranges.
  3. Update boundedAngle so that it sits inside the specified bounds of the angles.
  4. Convert the angle to a value, just as you converted it in setValue(_:animated:) before.
  5. Finally, set the knob control’s value to the calculated value.

Build and run your app; play around with your knob control to see the gesture recognizer in action. The pointer will follow your finger as you move it around the control – how cool is that? :]

Sending Action Notifications

As you move the pointer around, you’ll notice that the UISlider doesn’t update. You’ll wire this up using the target-action pattern which is an inherent part of UIControl.

Open ViewController.swift and add the following code to viewDidLoad:

knob.addTarget(self, action: "knobValueChanged:", forControlEvents: .ValueChanged)

This is the standard code you’ve used before to add a listener to a UIControl; here you’re listening for value-changed events.

Now actually implement the action method:

func knobValueChanged(knob: Knob) {
  valueSlider.value = knob.value
 
  updateLabel()
}

If the user changes the value on the knob control, then you’ll update the slider and the label.

Build and run your app; move the knob around and…nothing has changed. Whoops. You haven’t actually fired the event from within the knob control itself.

Time to fix that!

Inside the Knob class, add the following code to the end of handleRotation:

// Notify of value change
if continuous {
  sendActionsForControlEvents(.ValueChanged)
} else {
  // Only send an update if the gesture has completed
  if (gr.state == UIGestureRecognizerState.Ended) || (gr.state == UIGestureRecognizerState.Cancelled) {
    sendActionsForControlEvents(.ValueChanged)
  }
}

At the beginning of this tutorial you added the continuous property to the API so that the knob control API would resemble that of UISlider. This is the first and only place that you need to use it.

If continuous is set to true, then the event should be fired every time that the gesture sends an update, so call sendActionsForControlEvents:.

If continuous is set to false, then the event should only fire when the gesture ends or is cancelled. Since the control is only concerned with value changes, the only event you need to handle is UIControlEvents.ValueChanged.

Build and run your app again; move the knob around once again and you’ll see the UISlider move to match the value on the knob. Success!

Where to Go From Here?

Your knob control is now fully functional and you can drop it into your apps to enhance their look and feel. However, there are still a lot of ways that you could extend your control:

  • Add extra configuration options to the appearance of the control – perhaps you could allow an image to be used for the pointer.
  • Integrate a label displaying the current value of the control into the center of the knob.
  • Ensure that a user can only interact with the control if their first touch is on the pointer.
  • At the moment, if you resize the knob control, the layers won’t be re-rendered. You can add this functionality with just a few lines of code.

These suggestions are quite good fun, and will help you hone your skills with the different features of iOS you’ve encountered in this custom control tutorial. And the best part is that you can apply what you’ve learned in other controls that you build.

You can download a zip file of the completed project.

I’d love to hear your comments or questions in the forums below!

Custom Control Tutorial for iOS and Swift: A Reusable Knob is a post from: Ray Wenderlich

The post Custom Control Tutorial for iOS and Swift: A Reusable Knob appeared first on Ray Wenderlich.

iOS Apprentice Third Edition: Print Version Now Available!

$
0
0
The iOS Apprentice Third Edition is now available in print!

The iOS Apprentice Third Edition is now available in print!

Good news – the print version of Matthijs Hollemans’s popular book the iOS Apprentice Third Edition is now available!

This is the print version for the brand new third edition, which is fully updated for iOS 8 and Swift.

Since the book was previously in Objective-C, this is a massive update – an entirely new programming language!

Keep reading for some more pictures of the new book, learn about a discount for iOS Apprentice PDF customers, and enter for one last giveaway!

The Book

As you know, the iOS Apprentice is huge – over 800 pages! In fact it was so large that we had to make a few chapters downloadable so it would fit :]

Here’s a picture of what the book looks like next to my iPhone 5:

The iOS Apprentice Side View

The print quality came out great, and is easy to read and thumb through. Here’s what the inside of the book looks like:

The iOS Apprentice Inside

Note that on our store page, we have two options:

  • Just the PDF. Nothing’s changed here – if you prefer electronic books you can buy just the PDF same way you usually would.
  • The PDF + Print Version Bundle. This gives you the best of both worlds. It also saves you a lot of money – you’re effectively getting the printed books at a big discount compared to buying them separately.

Note we don’t offer an option for just the printed book. This is because we like to keep our books as up-to-date as we can, and we want to be able to give all our customers access to these updates (in PDF form). For example, iOS Apprentice PDF customers have received 4 major updates since it was first released (from iOS 4->5->6->7->8)!

Discount for iOS Apprentice PDF Customers – 1 Week Only

If you bought the PDF version of the iOS Apprentice, you are eligible for a special discount on the print version, to effectively “upgrade” your order to the PDF+Print bundle.

I will send all PDF version customers an email with instructions for how to upgrade your purchase in a few minutes.

If for some reason you do not get the email, just contact me and we’ll get it sorted.

Note this discount will expire in just 1 week, so if you’re interested snag it fast! :]

Back_Large

The Giveaway

One last thing. To celebrate the launch, we’re giving away a free copy of the book!

All you have to do to enter is leave a comment on this post – this Monday we’ll choose 1 lucky winner to get the free copy.

We already announced the winners of the other two giveaways, so this is your last chance to win a free print book :]

That’s it – we hope you all enjoy the print version of the iOS Apprentice Third Edition! If you’d like to pick up a copy just check out our store page.

Matthijs and I hope you enjoy the new third edition print version! :]

iOS Apprentice Third Edition: Print Version Now Available! is a post from: Ray Wenderlich

The post iOS Apprentice Third Edition: Print Version Now Available! appeared first on Ray Wenderlich.

Winners – Readers’ App Awards 2014!

$
0
0
Find out who won this year's Reader's Apps Awards!

Find out who won this year’s Reader’s Apps Awards!

Welcome to our third annual Readers’ Apps Awards, where we recognize some of the best apps made by raywenderlich.com readers in the past year.

You guys submitted over 500 apps this year, so I needed some help to choose the winners. We put together a team judges including Ray Wenderlich, Erik Kerber, Pierre Rochon, Chris Lowe, Nick Lockwood, and myself. We voted on the best apps for 7 categories:

  • Most Addicting: The game we couldn’t put down.
  • Most Hilarious Idea: The app that had us rolling.
  • Most Surprisingly Good: The app we never expected to steal our heart.
  • Most Technically Impressive: The app that made our inner programmer jealous.
  • Most Visually Impressive: The app that dropped our jaws.

We also chose 2 grand prize winners:

  • Best Reader’s Game: The overall best game of the year from a reader like you.
  • Best Reader’s App: The overall best app (non-game) of the year from a reader like you.

Its finally time – keep reading to see the best reader’s apps of 2014!

Most Addicting

Dash Up!dashup
Dash Up is easy to play, but not easy to put down. Bounce as high as you can, but you’ll have to avoid those pesky blocks. Plus, With some new game modes, even mastering the blocks isn’t the end.

Its blissfully simple. Its certainly addictive. Give it a shot and tell me when you beat my new high score: 16! ;]

“A fresh twist on the Flappy Bird concept – just as addicting and challenging. Just… one… more… try!” – Ray Wenderlich

“Easy to learn, hard to master. Or, in this case utterly infuriating to master, but addictive enough to make you keep trying. If you’re bored of Flappy Bird already, this could just be your next fix.” – Nick Lockwood



Most Hilarious Idea

OMG LOLomglol
Time to put your thumbs to the test. OMG LOL is a game for texters. Your phone gets blasted with texts and you’ve got to respond with the perfect emotion for each one.

Keep an eye out for the BRB bomb, everyone needs a quick break.

OMG this app is awesome, BRB after I play some more ;]

“This is the most unique and smart idea for an app I’ve seen in a long time. Very funny and the type that will make you want to show it off to your friends – well done!” – Ray Wenderlich

“Avoid social suicide with this text-tacular training tool! LOL, OMG and K your way through a conversation with your besties, but be careful not to say the wrong thing!” – Nick Lockwood

“This was a knockout good-for-a-quick-laugh app. It taught me ‘lol’ was not a proper response to ‘omg, I didn’t make the team!'” – Erik Kerber



Most Surprisingly Good

Music4KidsMusic4Kids
Teaching kids to read and write music has never been so fun. There are over 140 musical challenges to keep em busy and learning.

The controls are simple, just tap the notes in order to hear a fun song. But the fun doesn’t stop there, there is a composing section too where you can make your own music. Just tap on the bar where you’d like a note, and drag them around until you’ve created your masterpiece.

“Not only is Music4Kids fun to play around with, but it also has a surprising amount of educational depth to it – I learned a lot about music from it myself!” – Ray Wenderlich

“A cutesy and intuitive music-making tool: Fine tune your ear with the challenges, or satisfy your creative urges by composing your own tunes. Fun for kids and big kids alike.” – Nick Lockwood



Most Technically Impressive

Chiptunes ProChiptunes
Chiptunes lets you make music like the old days. Not with complicated instruments, but with universal circuits!

You can drag and drop any number of circuits to create an awesome tune. You’ve got tuners, filters, splitters, multipliers, and more. Everything you need to make awesome digital music.

“I never knew how Chiptunes actually worked before playing around with this app. Very cool and definitely got me in a retro vibe!” – Ray Wenderlich

“A crazy mashup of circuit design and music making: Wire together circuits to lay down some cool jams.” – Nick Lockwood

“SPICE meets garage band.” – Erik Kerber



Most Visually Impressive

Snowboard Partyscreen480x480
Snowboard Party puts snowboarding in the palms of your hands. Its got 75 levels, on 15 courses, with 15 achievements, and over 50 unique tricks to learn. You can customize your outfit and snowboard to give you an edge.

Play online with friends using GameCenter. Use an Apple certified GameController to get in some pro play.

This 3D high definition game will look great on your iPad or iPhone.

“Great looking app with a ton of polish and fun – just in time for snowboarding season!” – Ray Wenderlich

“If you’re looking for console-quality, AAA 3D gaming, look no further! Freestyle your way down the slopes, pulling tricks as you enjoy the stunning visuals and banging tunes.” – Nick Lockwood

“Beautiful, especially on the 6 Plus. Its really exciting to see “last gen” graphics on my iPhone.” – Erik Kerber

“Reminded me of SSX on the Wii. Was one of my favorite Wii games” – Pierre Rochon



Grand Prize Winners

Drum roll please… it’s time to announce our two Grand Prize winners!


Best Reader’s Game

Fruit DatingFruitDating03
Dating in the world of fruit is equally hard as it is for us. But you can help!

Fruit Dating is a sliding puzzle game where you’re a matchmaker, finding each piece of fruit find their soul mate.

It has adorable graphics and more than 60 well designed levels. Obstacles and tile types introduced in later levels bring the challenge.

Controls are simple, just a swipe is all it takes to bring fruit together. We all want one thing, more levels!

“A beautifully illustrated game that is simple to learn, and has some challenging and fun puzzles! A great example of how polish makes such a huge difference.” – Ray Wenderlich

“Not Tinder For Vegetables, as you might imagine, but a cutesy puzzle game where the aim is to match up fruit in series of challenging, tile-based levels.” – Nick Lockwood

“Very addictive, kept me coming back and wanting more.” – Pierre Rochon



Best Reader’s App

Quiver: The Programmer’s NotebookQuiver
Quiver is a Mac app designed for note takers. It lets you combine text and code snippets into notes.

Notes are organized by tags and grouped into notebooks. The entire note is editable inline anytime and your code snippets can be syntax highlighted in the language of your choice. Quiver supports super fast searching full text searching to find your notes fast.

And best of all, Quiver stores all your notes in plain JSON so you have full control of your content.

“I’ve often wished for a simplified version of Evernote tailored for a typical programmer’s needs, and Quiver fits that bill perfectly.” – Ray Wenderlich

“Note-Taking for Nerds: Finally, a note-making app that handles rich text and code without messing up your formatting. A handy HTML export option may make this your new favourite way to write documentation, or compose posts for your tech blog.” – Nick Lockwood

“As a coder, Quiver was great because markdown was just “one” of its text editing features. The LATEX integration really put it over the top as a notebook for me.” – Erik Kerber

“Can definitely see myself using this for all programming contracts” – Pierre Rochon


That’s All Folks!

Congratulations to all our winners! All winners get a free PDF of their choice from our site – we’ll be in touch to arrange delivery.

A special thanks to all our judges for helping me make the difficult picks. And a special thanks to you, all our readers. You’re the ones who make all this possible. Your incredible apps are why we write reviews at all. Looking forward to 2015!

Don’t forget, if you’ve made an app or game using our tutorials, we’d love to review your app too! Please submit it for next month.

As always, it was a load of fun playing with your apps and getting to see our tutorials in action! Its been another terrific year for apps and we can’t wait to see what you submit next year. Have a Merry Christmas!

Winners – Readers’ App Awards 2014! is a post from: Ray Wenderlich

The post Winners – Readers’ App Awards 2014! appeared first on Ray Wenderlich.

Viewing all 4373 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>