UIView in Swift — Part 1
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:
- Drawing:
UIView
is responsible for drawing its content on the screen. It has adraw(_:)
method that can be overridden to perform custom drawing. Alternatively, you can set thecontentMode
property to specify how the view should be drawn, and then set thebackgroundColor
,tintColor
, and other properties to customize its appearance. - Layout:
UIView
is responsible for laying out its subviews. It provides alayoutSubviews()
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. - Responding to user input:
UIView
is responsible for responding to user input events, such as touch events, by overriding methods such astouchesBegan(_:with:)
andtouchesEnded(_:with:)
. It also provides agestureRecognizers
property that allows you to attach gesture recognizers to the view to recognize more complex user interactions. - Managing subviews:
UIView
is responsible for managing its subviews by providing methods such asaddSubview(_:)
andremoveFromSuperview()
. You can also access its subviews using thesubviews
property. - Animating:
UIView
is responsible for animating its properties using built-in animation methods such asUIView.animate(withDuration:animations:)
. - Accessibility:
UIView
is responsible for providing accessibility information to assistive technologies such as VoiceOver. You can set theaccessibilityLabel
,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:
- 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.