Small Reading Glass Hard Case Snap Closure

In this tutorial you'll learn about SnapKit, a lightweight DSL (domain-specific language) to make Auto Layout and constraints a breeze to work with. You lot'll come abroad from this tutorial being able to write your layout code like a layout ninja would!

Auto Layout is a powerful tool to describe the relationships and constraints betwixt different views and complex view hierarchies in your awarding, simply writing these constraints can often exist quite non-intuitive at outset.

Up until a few years ago, writing these constraints programmatically was quite tedious with cryptic and verbose methods such as using Visual Formatting Language or manually creating NSLayoutConstraints.

iOS 9 greatly improved these mechanisms with the introduction of Layout Anchors, which make creating constraints quite intuitive and declarative. And all the same, at that place is still much to exist desired to make it even snappier for you lot to create constraints. This is exactly where SnapKit comes into play!

Getting Started

Throughout this tutorial, you'll piece of work on SnappyQuiz — a simple game where the role player gets random questions/statements and picks whether they're truthful or simulated.

Utilise the Download Materials push button at the top or lesser of this tutorial to download the starter project for this tutorial. Open SnappyQuiz.xcodeproj and wait for Xcode to fetch the SnapKit dependency.

The project comprises a few Swift files you'll need:

  • QuizViewController.swift: This is where the layout of the screen happens, including defining the views.
  • QuizViewController+Logic.swift: This file houses the logic of the game itself. You lot won't demand to modify this file in this tutorial.
  • QuizViewController+Constraints.swift: All of the constraints of the screen's UI are located in in this file, which is where you'll practice most of the work.

The projection besides includes Country.swift which represents the game state and Questions.swift where the raw question information is found, merely you won't really touch these during this tutorial.

Build and run the project. Y'all should meet the first question with a countdown timer ticking as well as a progress bar representing the electric current game progress:

SnappyQuiz running

In QuizViewController+Constraints.swift, explore setupConstraints(). This lawmaking uses the aforementioned Layout Anchors to define the relations between the dissimilar views in the app. You can find more about Layout Anchors in our Easier Auto Layout tutorial.

In this tutorial, you'll supplant each of these constraints with its SnapKit variation.

Snappin' & Chainin'

Before yous actually modify the SnappyQuiz application, it'south time for you to larn a bit more about what SnapKit actually is. In the introduction to this tutorial, I mentioned SnapKit uses a DSL, merely what does that actually mean?

What is a DSL?

A domain-specific linguistic communication (DSL) is a language created to limited and bargain with a specific domain or to solve a specific trouble.

In SnapKit's case, it aims to create a syntax that is much more than intuitive and easy-to-apply specifically for Automobile Layout constraints.

An important thing to understand is that, as a DSL, SnapKit is more often than not syntactic carbohydrate — you can do anything SnapKit does without SnapKit. Nonetheless, SnapKit provides a much more fluent and expressive syntax to solve this specific domain and trouble.

SnapKit Nuts

Take a very common set of constraints — attaching a view to all of its superview'due south edges:

Without SnapKit, the code would await like to the following:

kid.translatesAutoresizingMaskIntoConstraints = false  NSLayoutConstraint.activate([   child.leadingAnchor.constraint(equalTo: parent.leadingAnchor),   child.topAnchor.constraint(equalTo: parent.topAnchor),   child.trailingAnchor.constraint(equalTo: parent.trailingAnchor),   child.bottomAnchor.constraint(equalTo: parent.bottomAnchor), ])        

This is quite declarative, but SnapKit can exercise amend.

SnapKit introduces a namespace called snp on every UIView (and NSView, on macOS) in your system. That namespace, along with the makeConstraints(_:) method, are the essence of SnapKit.

SnapKit represents those constraints like this:

kid.snp.makeConstraints { make in   brand.leading.equalToSuperview()   make.top.equalToSuperview()   make.trailing.equalToSuperview()   make.bottom.equalToSuperview() }        

This might seem like a similar corporeality of lawmaking, but information technology greatly improves readability. Ii things you might've noticed are:

  1. You don't need to reference parent at all, cheers to SnapKit's equalToSuperview(). This means that, even if child moves to a different parent view, you won't demand to change this lawmaking.
  2. The make syntax create an almost-English language-like syntax, eastward.g. "make leading equal to superview", which is much nicer to read.

Composability & Chaining

You merely saw your first SnapKit code, but where SnapKit really shines is its limerick capabilities. You lot tin can chain whatever anchors together, likewise as the constraints themselves.

Y'all can rewrite the instance in a higher place as:

child.snp.makeConstraints { make in   make.leading.height.trailing.bottom.equalToSuperview() }        

Or even more concisely as:

kid.snp.makeConstraints { make in   brand.edges.equalToSuperview() }        

Want to add an inset of 16 to your view? Another elementary chaining volition get y'all at that place:

child.snp.makeConstraints { make in   brand.edges.equalToSuperview().inset(16) }        

As you tin see, composability and chaining are at the core of SnapKit and provide expressiveness y'all but tin can't achieve with vanilla NSLayoutConstraints.

Your First Constraints

Now that you have some of the basics of SnapKit, information technology'due south time for yous to convert all of the constraints in setupConstraints() to use information technology. Information technology's much simpler than you lot'd expect, and you'll go through these one-past-one, exploring SnapKit's various capabilities.

Go dorsum to QuizViewController+Constraints.swift and find setupConstraints(). You'll start modifying constraints below the updateProgress(to: 0) line. Y'all'll go back to the constraints to a higher place that line subsequently.

Detect the following block of code, defining constraints for the timer label:

lblTimer.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.actuate([   lblTimer.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.45),   lblTimer.heightAnchor.constraint(equalToConstant: 45),   lblTimer.topAnchor.constraint(equalTo: viewProgress.bottomAnchor, abiding: 32),   lblTimer.centerXAnchor.constraint(equalTo: view.centerXAnchor) ])        

Supercede information technology with the post-obit:

lblTimer.snp.makeConstraints { make in   make.width.equalToSuperview().multipliedBy(0.45) // one   make.summit.equalTo(45) // 2   make.top.equalTo(viewProgress.snp.bottom).offset(32) // 3   brand.centerX.equalToSuperview() // 4 }        

Like before, this is a straight translation of the original constraints, using SnapKit's chaining syntax. Quickly breaking this down:

  1. Make the label's width equal to the superview's width, multiplied by 0.45 (i.due east., 45% of the superview's width).
  2. Set the label's pinnacle to a static 45.
  3. Constrain the top of the label to the lesser of the progress bar, offset past 32.
  4. Heart the X axis to the superview'south 10 axis, making the label horizontally centered.

While not too different from the NSLayoutConstraint-based code, it provides much better readability and scoping of the views being constrained.

Annotation: Notice something else unlike? SnapKit no longer requires you to fix translatesAutoresizingMaskIntoConstraints to false! The library does it for you. No more forgetting to do that and tirelessly debugging messed up constraints :].

Practice That Again

On to the next UI element — the question label. Find the following code:

lblQuestion.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.actuate([   lblQuestion.topAnchor.constraint(equalTo: lblTimer.bottomAnchor, constant: 24),   lblQuestion.leadingAnchor     .constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),   lblQuestion.trailingAnchor     .constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16) ])        

There are three constraints here. Replacing these i-by-ane might feel familiar at this point. The first constraint could easily be translated to:

make.acme.equalTo(lblTimer.snp.bottom).outset(24)        

And the final two constraints could also be translated in the same direct way:

brand.leading.equalToSuperview().offset(xvi) brand.trailing.equalToSuperview().commencement(-16)        

But actually, did you notice these two constraints do the same affair for the leading and trailing anchors? Sounds like a prefect fit for some chaining! Supplant the entire lawmaking block from in a higher place with the post-obit:

lblQuestion.snp.makeConstraints { make in   brand.pinnacle.equalTo(lblTimer.snp.lesser).offset(24)   make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) }        

Note 2 things:

  1. The leading and trailing are chained, similar in previous examples.
  2. Yous don't have to always use snp to constrain views! Note how, this time, your code merely creates a constraint to a good ol' UILayoutGuide.

Another interesting fact is that the inset option doesn't accept to be numeric. Information technology can as well have a UIEdgeInsets struct. Y'all could rewrite the line above as:

make.leading.trailing.equalTo(view.safeAreaLayoutGuide)   .inset(UIEdgeInsets(top: xvi, left: 16, lesser: sixteen, right: sixteen))        

This might non be too useful here, simply tin can go extremely useful when the insets are different around the edges.

Two constraints downwards, three more to go!

A Quick Challenge!

The adjacent constraint is ane you've already seen earlier — the message label's edges should but equal to the superview'due south edges. Why don't you effort this one yourself?

If you're stuck, experience gratuitous to tap the button below to see the lawmaking:

[spoiler championship="Constrain edges to superview"]
Replace the following:

lblMessage.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.actuate([   lblMessage.topAnchor.constraint(equalTo: navView.topAnchor),   lblMessage.bottomAnchor.constraint(equalTo: navView.bottomAnchor),   lblMessage.leadingAnchor.constraint(equalTo: navView.leadingAnchor),   lblMessage.trailingAnchor.constraint(equalTo: navView.trailingAnchor) ])        

With:

lblMessage.snp.makeConstraints { make in   make.edges.equalToSuperview() }        

[/spoiler]

Last Constraint

There's nevertheless one final constraint to move to SnapKit's syntax. The horizontal UIStackView holding the Truthful and False buttons.

Observe the following code:

svButtons.translatesAutoresizingMaskIntoConstraints = fake NSLayoutConstraint.activate([   svButtons.leadingAnchor.constraint(equalTo: lblQuestion.leadingAnchor),   svButtons.trailingAnchor.constraint(equalTo: lblQuestion.trailingAnchor),   svButtons.topAnchor.constraint(equalTo: lblQuestion.bottomAnchor, abiding: sixteen),   svButtons.heightAnchor.constraint(equalToConstant: 80) ])        

Like before, the leading and abaft constraints could be chained since they are responsible for the same human relationship. Simply since y'all don't want to create a constraint to the superview, what should this look similar?

Replace the code above with the following:

svButtons.snp.makeConstraints { make in   brand.leading.trailing.equalTo(lblQuestion)   make.top.equalTo(lblQuestion.snp.bottom).offset(sixteen)   make.top.equalTo(eighty) }        

Notice the first line in the makeConstraints closure — you lot simply define that the leading and trailing constraints should equal to lblQuestion! No specificity needed! SnapKit is able to infer that you lot're referring to those specific constraints for lblQuestion.

This is too true for simpler constraints. The post-obit lawmaking:

view.snp.makeConstraints { make in   make.width.equalTo(otherView.snp.width)   brand.centerX.equalTo(otherView.snp.centerX) }        

Could be rewritten as:

view.snp.makeConstraints { make in   make.width.equalTo(otherView)   make.centerX.equalTo(otherView) }        

Annotation that the specificity of otherView is not needed — SnapKit knows what kind of constraints information technology needs to create based on the first view in the human relationship.

You could fifty-fifty farther reduce the code size by but writing:

view.snp.makeConstraints { brand in   brand.width.centerX.equalTo(otherView) }        

Wow! How cool is that?

Build and run the project. Y'all'll notice that it still works just equally information technology did earlier. Bully! :]

Swift mascot create UI layout on iPhone - SnapKit

Modifying Constraints

In the previous sections of this tutorial, yous learned about creating new constraints. But, sometimes you desire to modify an existing constraint.

Time to experiment with a few use cases where you lot might want to practise this, and how to achieve this inside SnapKit.

Updating a Constraint's Constant

Some of SnappyQuiz's users have been quite frustrated with how the app looks when switched to landscape orientation.

Yous can make information technology better by modifying some aspects of the UI when the app switches orientation, so yous'll exercise just that.

For this chore, you'll increase the elevation of the countdown timer in landscape orientation and also increment the font size. In this specific context, you lot need to update the constant of the timer characterization's elevation constraint.

When you lot're only interested in updating a constant, SnapKit has a super-useful method called updateConstraints(_:), which makes for a perfect fit here.

Back in QuizViewController+Constraints.swift, add the following code at the end of the file:

// Marking: - Orientation Transition Handling extension QuizViewController {   override func willTransition(     to newCollection: UITraitCollection,     with coordinator: UIViewControllerTransitionCoordinator     ) {     super.willTransition(to: newCollection, with: coordinator)     // i     let isPortrait = UIDevice.current.orientation.isPortrait          // ii     lblTimer.snp.updateConstraints { make in       brand.height.equalTo(isPortrait ? 45 : 65)     }          // 3     lblTimer.font = UIFont.systemFont(ofSize: isPortrait ? 20 : 32, weight: .light)   } }        

This adds an extension which will handle rotation of the view controller. Hither'south what the code does:

  1. Decide the electric current orientation of the device
  2. Use updateConstraints(_:) and update the timer characterization'southward height to 45 if it's in portrait — otherwise, you set it to 65.
  3. Finally, increment the font size appropriately depending on the orientation.

Idea information technology would exist hard? Sorry to disappoint you! ;]

Build and run the project. Once the app starts in the Simulator, press Command-Right Pointer or Control-Left Arrow to change the device orientation. Notice how the label increases its elevation and font size based on the device's orientation.

SnappyQuiz in landscape orientation

Remaking Constraints

Sometimes, y'all'll need more than just modifying a few constants. You lot might want to completely modify the unabridged constraint assail a specific view. For that very common case, SnapKit has another useful method called — yous guessed it — remakeConstraints(_:).

There's a perfect place to experiment with this method inside SnappyQuiz: the progress bar on tiptop. Right now, the progress bar's width constraint is saved in a variable called progressConstraints in QuizViewController.swift. Then, updateProgress(to:) but destroys the old constraint and creates a new 1.

Time to see if you can make this mess a fleck ameliorate.

Dorsum in QuizViewController+Constraints.swift, accept a look at updateProgress(to:). It checks if at that place is already a constraint and, if so, deactivates it. So, it creates a new constraint and activates it.

Replace updateProgress(to:) with the following:

func updateProgress(to progress: Double) {   viewProgress.snp.remakeConstraints { make in     make.tiptop.equalTo(view.safeAreaLayoutGuide)     make.width.equalToSuperview().multipliedBy(progress)     make.peak.equalTo(32)     make.leading.equalToSuperview()   } }        

Whoa, this is much nicer! That entire somewhat-cryptic lawmaking piece was entirely replaced with just a few lines of code. remakeConstraints(_:) simply replaces the entire constraint set every fourth dimension, and then you don't have to manually reference the constraints and manage them.

Another upside of this is that you lot can further clean up some of the mess in the current lawmaking.

In setupConstraints(), remove the post-obit code:

guard let navView = navigationController?.view else { return }  viewProgress.translatesAutoresizingMaskIntoConstraints = false  NSLayoutConstraint.activate([   viewProgress.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),   viewProgress.heightAnchor.constraint(equalToConstant: 32),   viewProgress.leadingAnchor.constraint(equalTo: view.leadingAnchor) ])        

The commencement line in that method should now be simply updateProgress(to: 0).

Finally, yous can get rid of the following lines in QuizViewController.swift:

/// Progress bar constraint var progressConstraint: NSLayoutConstraint!        

All washed! Build and run your app, and everything should work equally before, but with a much clearer constraints management code.

Keeping a Reference

While yous won't experiment with this option in SnappyQuiz, it's however ane you should know of.

In standard NSLayoutConstraint manner, you can store a reference to your constraint and modify information technology later on. That'southward also possible with SnapKit, using the Constraint type:

var topConstraint: Constraint?  lblTimer.snp.makeConstraints { make in    // Shop your constraint   self.topConstraint = make.acme.equalToSuperview().inset(16)   brand.leading.abaft.bottom.equalToSuperView() }  // Which you lot tin can later change self.topConstraint?.update(inset: 32)  // Or entirely deactivate self.topConstraint?.deactivate()        

When Things Go Wrong

Sometimes in life, things get wrong. This is fifty-fifty more than often the example when talking almost Auto Layout constraints.

Back in QuizViewController+Constraints.swift, find the post-obit line:

make.centerX.equalToSuperview()        

Right below information technology, but all the same inside the makeConstraints closure, add:

make.centerY.equalToSuperview()        

Build and run the app. As y'all can run across, the UI is entirely broken:

App with broken constraints

Also, as expected, y'all'll see a giant wall of cleaved constraints in your debug panel, which should look similar to the following:

[LayoutConstraints] Unable to simultaneously satisfy constraints.  "<SnapKit.LayoutConstraint:0x600001b251a0@QuizViewController+Constraints.swift#62 UIView:0x7f9371e004a0.top == UILayoutGuide:0x60000062c0e0.summit>",     "<SnapKit.LayoutConstraint:0x600001b25260@QuizViewController+Constraints.swift#64 UIView:0x7f9371e004a0.height == 32.0>",     "<SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7f9371e088c0.peak == 45.0>",     "<SnapKit.LayoutConstraint:0x600001b2dce0@QuizViewController+Constraints.swift#39 UILabel:0x7f9371e088c0.top == UIView:0x7f9371e004a0.lesser + 32.0>",     "<SnapKit.LayoutConstraint:0x600001b2dda0@QuizViewController+Constraints.swift#41 UILabel:0x7f9371e088c0.centerY == UIView:0x7f9371e09a50.centerY>",     "<NSLayoutConstraint:0x600001c6c2d0 'UIView-Encapsulated-Layout-Height' UIView:0x7f9371e09a50.height == 551   (active)>",     "<NSLayoutConstraint:0x600001c61450 'UIViewSafeAreaLayoutGuide-top' 5:|-(0)-[UILayoutGuide:0x60000062c0e0'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x7f9371e09a50 )>"  Will attempt to recover past breaking constraint  <SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.tiptop == 45.0>        

Oh boy. Where do you even kickoff? All yous see is a bunch of memory addresses that don't necessarily mean besides much. It's also quite difficult to understand which constraints were broken.

Luckily, SnapKit provides a corking additional modifier to track downwardly these sort of bug, called labeled(_:).

Replace the unabridged lblTimer constraint cake with the following:

lblTimer.snp.makeConstraints { make in   make.width.equalToSuperview().multipliedBy(0.45).labeled("timerWidth")   make.height.equalTo(45).labeled("timerHeight")   make.top.equalTo(viewProgress.snp.bottom).kickoff(32).labeled("timerTop")   make.centerX.equalToSuperview().labeled("timerCenterX")   brand.centerY.equalToSuperview().labeled("timerCenterY") }        

Noticed the labeled(_:) add-on on every constraint? This lets y'all attach a descriptive title for every constraint, so y'all don't have to pick through retentivity addresses and lose your sanity.

Build and run your app one final time. Your broken constraints should provide much clearer information at this signal:

[LayoutConstraints] Unable to simultaneously satisfy constraints.  "<SnapKit.LayoutConstraint:0x60000365c4e0@QuizViewController+Constraints.swift#62 UIView:0x7fc53e4181d0.top == UILayoutGuide:0x600002b0ae60.top>", "<SnapKit.LayoutConstraint:0x60000365e8e0@QuizViewController+Constraints.swift#64 UIView:0x7fc53e4181d0.pinnacle == 32.0>", "<SnapKit.LayoutConstraint:timerCenterY@QuizViewController+Constraints.swift#41 UILabel:0x7fc53e41d060.centerY == UIView:0x7fc4fe507170.centerY>", "<SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>", "<SnapKit.LayoutConstraint:timerTop@QuizViewController+Constraints.swift#39 UILabel:0x7fc53e41d060.top == UIView:0x7fc53e4181d0.lesser + 32.0>", "<NSLayoutConstraint:0x6000031346e0 'UIView-Encapsulated-Layout-Summit' UIView:0x7fc4fe507170.superlative == 551   (active)>", "<NSLayoutConstraint:0x600003139c70 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x600002b0ae60'UIViewSafeAreaLayoutGuide']   (active, names: '|':UIView:0x7fc4fe507170 )>"  Will attempt to recover by breaking constraint  <SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.peak == 45.0>        

This looks similar, merely look advisedly. You can meet gems like timerCenterY. This is much more informative, and you accept some groovy labeled constraints to start debugging your manner through.

More specifically, the only three labels y'all tin recognize in this output are timerCenterY, timerHeight and timerTop. Since the height is static, y'all can be sure the conflict is between the 2 constraints left. That narrowed things down much faster than picking through the original mess of Car Layout debugging output!

One time you're washed, feel gratuitous to remove the centerY constraint that started this mess.

Where to Go From Here?

Congratulations! You now know almost of what SnapKit has to offer, merely there are still a few features and modifiers you lot should look into, such as priority, divided and more. Check out SnapKit's official GitHub repo for more than data.

Call up, SnapKit is there to assistance you by creating an like shooting fish in a barrel-to-consume, problem-specific syntax for creating constraints, only it doesn't provide features that can't be achieved with regular NSLayoutConstraints. Experience free to experiment with both and find a proficient middle ground that works for each scenario.

We hope you've enjoyed this tutorial. Got any more questions or … constraints ? Leave a annotate in the forum thread below.

mcbridesubsting2001.blogspot.com

Source: https://www.raywenderlich.com/3225401-snapkit-for-ios-constraints-in-a-snap

0 Response to "Small Reading Glass Hard Case Snap Closure"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel