UIView in Swift — Part 1

Janvi Arora
11 min readMar 11, 2023

--

Views are the fundamental building blocks of your app’s user interface, and the UIView class defines the common behaviours of all views. A view object renders content within its bounds rectangle and handles any interactions with that content.

Declaration:

@MainActor class UIView : UIResponder

In Swift, the @MainActor attribute is used to mark a function, method, or class as being designed to run on the main (UI) thread. When a function or method is marked with @MainActor, the compiler ensures that any calls to that function or method are also dispatched to the main thread.

In the case of the UIView class, marking it with @MainActor means that any methods defined on the class (or inherited from its superclass, UIResponder) that are marked with @MainActor will be automatically dispatched to the main thread when called.

UIView is a class in the iOS UIKit framework that represents a rectangular area on the screen, and provides basic functionality for drawing and managing user interface elements. As a subclass of UIResponder, it also provides methods for handling user input events such as touch events.

By marking the UIView class with @MainActor, any methods on the class that are marked with @MainActor will be executed on the main thread. This can be useful because all user interface updates must be performed on the main thread in iOS, to ensure smooth and responsive user interfaces.

For example, if a method on UIView that updates the view's background color is marked with @MainActor, any calls to that method will be automatically dispatched to the main thread, ensuring that the update is performed on the correct thread.

Hierarchy:

Object ⇾ NSObject ⇾ UIResponder ⇾ UIView

Responsibilities of UIView:

  1. Drawing: UIView is responsible for drawing its content on the screen. It has a draw(_:) method that can be overridden to perform custom drawing. Alternatively, you can set the contentMode property to specify how the view should be drawn, and then set the backgroundColor, tintColor, and other properties to customize its appearance.
  2. Layout: UIView is responsible for laying out its subviews. It provides a layoutSubviews() method that can be overridden to perform custom layout. You can also use constraints to specify how the view and its subviews should be laid out relative to each other.
  3. Responding to user input: UIView is responsible for responding to user input events, such as touch events, by overriding methods such as touchesBegan(_:with:) and touchesEnded(_:with:). It also provides a gestureRecognizers property that allows you to attach gesture recognizers to the view to recognize more complex user interactions.
  4. Managing subviews: UIView is responsible for managing its subviews by providing methods such as addSubview(_:) and removeFromSuperview(). You can also access its subviews using the subviews property.
  5. Animating: UIView is responsible for animating its properties using built-in animation methods such as UIView.animate(withDuration:animations:).
  6. Accessibility: UIView is responsible for providing accessibility information to assistive technologies such as VoiceOver. You can set the accessibilityLabel, accessibilityHint, and other properties to provide meaningful descriptions of the view and its contents.

Creating a view:

let rect = CGRect(x: 10, y: 10, width: 100, height: 100)
let myView = UIView(frame: rect)

Creating an object of UIView:

init(frame: CGRect)

init(frame: CGRect) is a designated initializer in the UIView class that creates and initializes a new instance of UIView with the specified frame rectangle. The frame parameter is a CGRect that defines the position and size of the view in its superview's coordinate system. This initializer is commonly used to create a new view and set its initial position and size, as well as customize its appearance and behavior.

Returns an initialized view object.

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
// Decode any custom properties or attributes of the view
// and initialize them appropriately
}

init?(coder: NSCoder) is an initializer in the UIView class that is used for initializing a view object from data that was previously archived. This method is called when you create a view object from a storyboard or from a XIB file.

The coder parameter is an instance of NSCoder, which is responsible for decoding the archived data and creating the view object. The initializer returns an optional instance of the view class, which can be nil if the decoding process fails.

When implementing init?(coder: NSCoder), you need to make sure that all the necessary properties and attributes of the view are decoded and initialized correctly, in order to recreate the view object accurately.

Configuring view’s visual appearance:

  1. var backgroundColor: UIColor?

The view’s background color.

// Declaration
@NSCopying var backgroundColor: UIColor? { get set }

Changes to this property can be animated.

Default value = nil (which results in a transparent background color)

2. var isHidden: Bool

A Boolean value that determines whether the view is hidden.

// Declaration
var isHidden: Bool { get set }

Default value = false

If set to true, it hides the receiver.

If set to false, it shows the receiver.

A hidden view disappears from its window and does not receive input events.

“Hiding a view with subviews has the effect of hiding those subviews and any view descendants they might have” means that when you set the isHidden property of a view to true, all its subviews and any subviews of its subviews, and so on, also become hidden. This is because the subviews of a view are positioned inside the bounds of the parent view, and therefore are not visible when the parent view is hidden.

If you hide a parent view that contains subviews, and then later show the parent view again, the hidden state of the subviews remains unchanged. You would need to explicitly set the isHidden property of each subview to false in order to make them visible again.

3. var alpha: CGFloat

The view’s alpha value that determines the opacity of the view and its subviews. The alpha property is a floating-point value that ranges from 0.0 (fully transparent) to 1.0 (fully opaque).

// Declaration
var alpha: CGFloat { get set }

The alpha property is often used to create interesting visual effects or to indicate the state of a view. For example, you could use the alpha property to dim a view when a user interacts with it, or to make a view partially transparent when you want to overlay it on top of another view.

Default value = 1.0 (fully opaque)

4. var isOpaque: Bool

A Boolean value that determines whether the view is opaque.

// Declaration
var isOpaque: Bool { get set }

Default value = true

By default, the isOpaque property is set to true if the view's alpha value is set to 1.0 (fully opaque), and false if the alpha value is set to anything less than 1.0 (partially transparent). This is because a view with an alpha value of less than 1.0 may have transparent pixels, and therefore is not fully opaque.

If the isOpaque property is set to true, the view's contents are assumed to be fully opaque, and the system can optimize rendering performance by not drawing any content behind the view. However, if the isOpaque property is set to false, the system must perform additional rendering steps to blend the view's contents with the underlying content.

5. var tintColor: UIColor!

The first nondefault tint color value in the view’s hierarchy, ascending from and starting with the view itself.

// Declaration
var tintColor: UIColor! { get set }

The tint color affects certain visual elements of the view and its subviews, such as the color of the text in a button or label, or the color of the outline of a segmented control.

You can also set the tint color globally for your app using the UIAppearance protocol, which allows you to customize the appearance of certain UIKit controls across your app. For example, you might set the tint color for all buttons to blue using the following code:

// Set the tint color for all buttons to blue
UIButton.appearance().tintColor = UIColor.blue

The default value of the tintColor property in UIView is nil. This means that if you do not explicitly set the tintColor property of a view, it will not have a tint color and will use its default appearance for its content and subviews.

6. var tintAdjustmentMode: UIView.tintAdjustmentMode

In Swift, the tintAdjustmentMode property of a UIView determines whether the view and its subviews should be adjusted to reflect the current tint color. The tintAdjustmentMode property is an enum value of type UIView.TintAdjustmentMode that specifies the adjustment mode.

enum TintAdjustmentMode : Int, @unchecked Sendable {
case automatic
case normal
case dimmed
}

You can also set the tint adjustment mode globally for your app using the UIAppearance protocol, which allows you to customize the appearance of certain UIKit controls across your app. For example, you might set the tint adjustment mode for all buttons to .dimmed using the following code:

// Set the tint adjustment mode for all buttons to dimmed
UIButton.appearance().tintAdjustmentMode = .dimmed

Default value = .automatic (which means that the view and its subviews will adjust their appearance to reflect the view’s tintColor property.)

7. var clipsToBounds: Bool

In Swift, the clipsToBounds property of a UIView determines whether subviews that extend outside of the view's bounds should be clipped or not.

The clipsToBounds property affects how the view and its subviews are displayed when they extend beyond the bounds of the view. If clipsToBounds is set to true, any subviews that extend outside of the view's bounds will be clipped and not displayed. If clipsToBounds is set to false, any subviews that extend outside of the view's bounds will be visible.

// Set the clips to bounds property for all views to true
// Gloabally setting the property
UIView.appearance().clipsToBounds = true

Default value = false

NOTE: Some subclasses of UIView, like UIScrollView, override the default value to true.

8. var clearsContextBeforeDrawing: Bool

In Swift, the clearsContextBeforeDrawing property of a UIView determines whether the view should clear its graphics context before drawing its content or not. The clearsContextBeforeDrawing property is a Boolean value that specifies whether the view should erase its current drawing before it starts drawing new content.

If set to true, the view will clear its graphics context before drawing its content, effectively erasing any previous drawings on the view.

The clearsContextBeforeDrawing property can affect the performance of your app, as clearing the graphics context before drawing new content can be an expensive operation. In general, it is best to set the clearsContextBeforeDrawing property to false unless you specifically need to erase the view's previous content before drawing new content.

NOTE: clearsContextBeforeDrawing property only applies when the view's draw(_:) method is called, which is typically called when the view needs to be redrawn. The draw(_:) method is not called automatically and must be called explicitly, either by calling the setNeedsDisplay() method or by implementing custom drawing code in the view's draw(_:) method.

Default value = true

// Get the default value of clearsContextBeforeDrawing
let defaultClearsContextBeforeDrawing = UIView().clearsContextBeforeDrawing

// defaultClearsContextBeforeDrawing will be true here i.e. default value

9. var mask: UIView?

In Swift, the mask property of a UIView is used to create a clipping mask that can be used to selectively show or hide parts of the view's content. The mask property is a reference to another UIView that serves as the mask for the view.

The mask view’s alpha channel is used to determine which parts of the view’s content should be visible and which parts should be hidden. The alpha channel of the mask view is mapped onto the view’s content, and any pixels with an alpha value of less than 1.0 are treated as transparent and are not shown.

Here’s an example of how to set the mask property of a view:

// Create a mask view
let maskView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

// Set the mask view's background color to black
maskView.backgroundColor = UIColor.black

// Set the mask property of myView to the maskView
myView.mask = maskView

In this example, maskView is created and set as the mask for myView. Any parts of myView's content that fall outside of the bounds of maskView will be hidden.

The mask property can be used to create a wide range of effects, such as rounded corners, custom shapes, and cutout views. However, it is important to note that using a complex mask can have a performance impact on your app, as the mask needs to be recalculated every time the view is redrawn.

10. class var layerClass: AnyClass

layerClass property is used to specify the class of the layer that backs a UIView. By default, a UIView's layer is an instance of the CALayer class, but you can specify a different layer class by overriding the layerClass property in a custom view subclass.

For example, if you want to use a CAShapeLayer instead of a CALayer as the backing layer for your custom view, you could override the layerClass property like this:

class MyView: UIView {
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
}

Now, whenever an instance of MyView is created, it will have a CAShapeLayer as its backing layer instead of a CALayer.

Specifying a custom layer class can be useful if you need to perform advanced drawing operations or add custom animations to your views. However, it’s important to note that overriding the layerClass property can have performance implications, as some layer classes may be slower or more resource-intensive than others. Additionally, some layer classes may not be compatible with certain features or behaviors of UIView, so it's important to test your custom view thoroughly to ensure that it behaves as expected.

The default value of the layerClass property in Swift for a UIView instance is the CALayer class.

11. var layer: CALayer

In Swift, every instance of UIView has a corresponding layer object that handles the actual rendering of the view's content. The layer is an instance of the CALayer class, which is part of the Core Animation framework. The layer is responsible for drawing the content of the view, handling animations, and providing advanced visual effects such as shadows, borders, and gradients.

The UIView class provides a number of methods and properties that allow you to work with the layer object directly. For example, the layer property provides direct access to the layer object, allowing you to modify its properties or add custom sublayers as needed.

Here are some common tasks you can perform with the layer property in Swift:

  • Set the background color of the layer: view.layer.backgroundColor = UIColor.red.cgColor
  • Add a border to the layer: view.layer.borderWidth = 1.0; view.layer.borderColor = UIColor.black.cgColor
  • Add a shadow to the layer: view.layer.shadowColor = UIColor.black.cgColor; view.layer.shadowOpacity = 0.5; view.layer.shadowRadius = 5.0; view.layer.shadowOffset = CGSize(width: 0, height: 2)
  • Add a sublayer to the layer: let sublayer = CALayer(); view.layer.addSublayer(sublayer)

It’s important to note that while the layer and the view are closely related, they are not the same thing. The layer is responsible for rendering the view’s content, but the view is responsible for handling user interactions and managing its subviews. Additionally, modifying the layer’s properties directly can have unexpected results if you’re not familiar with how Core Animation works. In general, it’s best to use the UIView class's high-level methods and properties whenever possible, and only work with the layer object directly when you need to perform advanced customizations or optimizations.

This property is never nil.

NOTE By Apple: Because the view is the layer’s delegate, never make the view the delegate of another CALayer object. Additionally, never change the delegate of this layer object.

Explanation:

In Swift, the UIView class is the delegate of its corresponding CALayer object by default. This means that the layer object can communicate with the view object through the delegate methods to get information about the view's properties and state.

It is important to note that, as a general rule, you should not make a UIView object the delegate of another CALayer object. This is because the view is already the delegate of its own layer object, and making it the delegate of another layer object could lead to conflicts or unexpected behavior.

Furthermore, you should never change the delegate of the CALayer object directly. This is because the view and its layer have a close relationship, and changing the delegate could cause the view to stop functioning properly. Instead, if you need to modify the behavior of the layer, you can subclass CALayer and override its methods and properties to achieve the desired effect.

In summary, the UIView and its layer are closely intertwined, and should not be separated by making the view the delegate of another layer object or changing the delegate of the layer object directly. Instead, it's best to work within the established relationship between the view and its layer, and use the UIView class's high-level methods and properties whenever possible.

--

--

No responses yet