Skip to main content

19 questions tagged with "UIKit"

UIKit tag description

View All Tags

How are Content Hugging and Content Compression Resistance different?

· 3 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources
TL/DR
  • Content Hugging: Controls how much a view resists being stretched beyond its natural size. Higher priority = more resistance to growth.
  • Content Compression Resistance: Controls how much a view resists being shrunk below its natural size. Higher priority = more resistance to shrinking.

At its core, AutoLayout is a constraint solver. It will take some number of views and their constraints and try to formulate an arrangement that satisfies all of them.

Sometimes in this process, AutoLayout will make a view smaller than you’d like or may make it larger than you intend it to be.

In situations like this, we can leverage Content Hugging and Content Compression Resistance for more granular control over how AutoLayout resizes our views.

In order to understand how they work, we need to understand intrinsic content size. This is the minimum size views want to be to show all of their content. For example, views like UIImageViews, UIButtons, and UILabels all know what their size should be in order to accommodate the content they’re meant to show.

Content Hugging Resistance represents how hard a view is going to fight against being made larger than its intrinsic content size. The higher the priority, the harder it’s going to resist growing.

Conversely, Content Compression Resistance represents how hard a view is going to fight against being made smaller than its intrinsic content size. The higher the priority, the harder it's going to resist shrinking.

So, when AutoLayout is trying to resolve constraints, it’s going to look at these properties and their respective priorities to figure out which views it can make larger and which views it can make smaller.

In Bullets
  • Content Hugging

    • Definition: Measures how strongly a view resists growing larger than its intrinsic content size.
    • Priority Impact: Higher priority means the view resists being stretched more.
    • Example: A UILabel with high content hugging priority will avoid expanding beyond its text's natural width.
  • Content Compression Resistance

    • Definition: Measures how strongly a view resists shrinking smaller than its intrinsic content size.
    • Priority Impact: Higher priority means the view resists being compressed more.
    • Example: A UIButton with high content compression resistance will avoid collapsing below its text's natural size.
  • Usage in AutoLayout

    • AutoLayout uses these properties to balance view resizing based on constraints.
    • Content Hugging and Compression Resistance priorities guide how views are resized when constraints are ambiguous or conflicting.

How would you animate a view that has a constraint?

· 2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

With AutoLayout, you can simply change a view’s constraints and let the system figure out how to resize that view and all of its neighbors.

In interviews, I often see candidates mix AutoLayout constraint changes with manual changes to a view’s frame. This combination of two different layout paradigms really complicates the logic and is often error-prone. I’d recommend you stick with AutoLayout whenever possible.

Now, assuming you have a reference to the constraint you want to manipulate, you can simply animate a change by updating the constraint’s value like so:

imageViewHeightConstraint.constant = 80

UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}

Make sure you update the constraint outside of the animation block!

Remember to call layoutIfNeeded() on self.view and not on the view you are attempting to animate. Otherwise, the changes in layout will be applied without animation.

Additionally, Apple recommends calling layoutIfNeeded() once before the animation block to ensure all pending layout operations are completed.

It’s very important that we call layoutIfNeeded() on self.view and not just on the view we are trying to animate. By calling this function on self.view,the animation and layout changes will “trickle down” through all of the other subviews.

Remember, you want to animate changes to the neighboring view’s layouts as well - not just changes to any one particular subview.

On what thread should all UI updates be made?

· 2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

All UI updates should be performed on the Main Thread.

When your application launches, the UIApplication is set up on the Main Thread. From here, all of the views in your app ultimately descend from this UIApplication instance.

This means that any interaction with a UIButton or changes to the text in a UILabel have to be performed on the Main Thread as the application itself is configured to run on the Main Thread.

Furthermore, pending changes to the UI are not applied immediately. They are drawn at the end of the thread’s run loop. So, to ensure all of the UI updates are applied together (and are applied without any flickering or visual side effects), it’s important to have them all queued on the same thread.

If we had multiple threads of varying priorities and computational abilities performing UI updates, then the UI would flicker or be unpredictable as each thread would be changing the UI independently of the state reflected in another thread.

Moreover, it’s important to remember that the Main Thread is far more performant than all other threads. If we were to update our UI on the background thread (which has a lower priority and limited resources), the task would first be added to a queue of other potentially long-running tasks (i.e. downloading a file). Now, our UI update will not be applied until all of the other higher priority and previously queued tasks finish executing.

To avoid that poor user experience and to keep things simple, we delegate all of the “real-time” activity to the Main Thread. Since the Main Thread has so much more computational ability, delegating UI-centric tasks to it helps ensure the system applies these UI updates as fast as possible.

What are layer objects?

· One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Every UIView has a CALayer property which is responsible for the actual rendering of visual content and animations.

That’s why we’ll often write code like this:

layer.shadowOpacity = 0.3
layer.shadowRadius = 2
layer.shadowOffset = CGSize(width: 0 , height: 2 )
layer.borderWidth = 0

We’re giving instructions directly to the rendering component of the UIView.

There is an important distinction between the UIView and the CALayer. The UIView takes care of its own layout and placement, but it is the CALayer that is responsible for rendering its actual contents, including borders, shadows, corner radius, complex animations, and other visual effects.

What are the differences between a .xib and a .storyboard file?

· One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Both a .xib and .storyboard are stored as XML filesand are both converted to binary files - .nibs- at compile time.

A .xib file usually specifies one UIViewController or standalone view whereas a .storyboard specifies a set of UIViewControllers and the navigation behavior between them.

What does an unwind segue do?

· One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

You can use an unwind segue to jump to any UIViewController further up your UIViewController hierarchy while simultaneously destroying all other UIViewControllers in between. More specifically, you can use it to navigate back through one or more push, modal, or popover segues.

Let’s say you’re navigating from ViewControllerA → ViewControllerB → ViewControllerC and you want to go from ViewControllerC back to ViewControllerA.If you had to rely on just the back button alone, you’d have to go through ViewControllerB first.

With an unwind segue, we can jump directly to ViewControllerA and destroy ViewControllerB along the way.

In ViewControllerA, add:

@IBAction func unwindToViewControllerA(segue: UIStoryboardSegue) { }

You can leave the method body empty.

Then, in the UIViewController you’re departing from ViewControllerC you can drag from the body of the UIViewController to the Exit Icon and select the function we’ve created.

Now, when we press the “Go Directly to A” button, the application will navigate directly from ViewControllerC to ViewControllerA skipping over anddestroyingViewControllerB along the way.

What does layoutSubviews() do?

· One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

The default implementation of this function uses any constraints you have set to determine the size and position of all of the view’s subviews. Overriding this method allows you to perform a more precise layout of a view’s subviews by setting the frame rectangles of your subviews directly.

Typically, you would only override this function if AutoLayout wasn’t offering the behavior you wanted.

According to Apple’s documentation:

You should not call this method directly. If you want to force a layout update, call the setNeedsLayout() method instead to do so prior tothe next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded() method.

What does setNeedsDisplay() do?

· 2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Just like setNeedsLayout() tells the system the layout needs to be updated, setNeedsDisplay() informs the system that the view’scontent needs to be redrawn. This function queues the redrawing task and returns immediately. The view is only redrawn at the next drawing cycle at which point any and all views are updated.

Typically, you will only need to call setNeedsDisplay() if you are also overriding drawRect() in your implementation. As would be the case if you were working on a customUIControlor if your view contained some custom shapes or effects.

drawRect() is responsible for the actual rendering of the view and is called by the system whenever drawing is required. You should never call this function explicitly; letting the system manage the calls to this function helps avoid multiple redraws if one has already been queued.

Let’s say we have a DrawLineView which draws a linebetween 2 provided points. It might look something like this:

class DrawLineView: UIView {
var point1, point2: CGPoint!
// Only override drawRect() if you perform custom drawing.
// An empty implementation adversely affects performance
// during animation.
override func drawRect(rect: CGRect) {
// Draws a line from point1 to point2
let context = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(context, point1.x, point1.y)
CGContextAddLineToPoint(context, point2.x,point2.y)
CGContextStrokePath(context)
}
}

If we wanted to update the location and the length of the line, simply updating the values of point1 and point2 will not suffice. Changing these properties will not automatically redraw the line with updated starting and ending points. So far all we’ve done is change the underlying data, but we still haven’t forced an update of the UI [forced a call todrawRect()].

As a result, we’ll need to call setNeedsDisplay() after updating point1 and point2:

drawLineView.point1 = CGPointMake(startDot.center.x, startDot.center.y);
drawLineView.point2 = CGPointMake(endDot.center.x, endDot.center.y);

drawLineView.setNeedsDisplay()

This call tosetNeedsDisplay()will in turn calldrawRect()which will actually redraw the line to reflect its new starting and ending locations.

Here’s an example courtesy offujianjin6471 on GitHub. It may help to check out this project and play around with it to better understand setNeedsDisplay()’s role.

What does viewDidLayoutSubviews() do?

· 2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Simply put, viewDidLayoutSubviews() allows you to make customizations to views after they’ve been positioned by AutoLayout, but before they are visible to the user.

Whenever the bounds change for a UIViewController’s view (i.e. device rotation), it’s likely that the position and size of all the subviews will need to be updated as well. So, the system will calllayoutSubviews() to perform this change.

Then, once your UIViewController has finished laying out all of its subviews (all of your subviews are in their correct location and their frames honor whatever AutoLayout constraints you’ve specified), the system will call viewDidLayoutSubviews().

From here, if you need to further customize or override any changes prior to the view being visible on screen, viewDidLayoutSubviews() gives youan opportunity to do so.

If you’re wondering why we can’t make these changes in viewDidLoad() or viewWillAppear(), it’s because the frames of the subviews aren’t finalized by the time those functions are called. They’re only considered finalized once layoutSubviews() finishes running, so our only option to make customizations is in viewDidLayoutSubviews().

The order of the UIViewController lifecycle is as follows:

  1. loadView()
  2. viewDidLoad()
  3. viewWillAppear()
  4. viewWillLayoutSubviews()
  5. viewDidLayoutSubviews()
  6. viewDidAppear()
  7. viewWillDisappear()
  8. viewDidDisappear()

What is a placeholder constraint?

· One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

There may be cases where a constraint can only meaningfully be set at runtime. Perhaps you don't know which subview to constrain a constraint too or maybe you don't know what the constant value of the constraint should be.

If you choose to omit this constraint at compile time, AutoLayout might complain about ambiguous constraints or an unsatisfiable layout. By using placeholder constraints, we can sidestep this issue.

Simply put, a placeholder constraint is a constraint that exists only at design time. They are not included in the layout when the app runs.

You typically add placeholder constraints when you plan to dynamically add constraints at runtime. By temporarily adding the constraints needed to create a non-ambiguous, satisfiable layout, you clear out any warnings or errors in Interface Builder.