Slather logo

Coverage for "ImageMaskedView.swift" : 0.00%

(0 of 60 relevant lines covered)

ChatLayout/Classes/Extras/ImageMaskedView.swift

1
//
2
// ChatLayout
3
// ImageMaskedView.swift
4
// https://github.com/ekazaev/ChatLayout
5
//
6
// Created by Eugene Kazaev in 2020-2022.
7
// Distributed under the MIT license.
8
//
9
// Become a sponsor:
10
// https://github.com/sponsors/ekazaev
11
//
12
13
import Foundation
14
import UIKit
15
16
/// A transformation to apply to the `ImageMaskedView.maskingImage`
17
public enum ImageMaskedViewTransformation {
18
19
    /// Keep image as it is.
20
    case asIs
21
22
    /// Flip image vertically.
23
    case flippedVertically
24
25
}
26
27
/// A container view that masks its contained view with an image provided.
28
public final class ImageMaskedView<CustomView: UIView>: UIView {
29
30
    /// Contained view.
31
    public lazy var customView = CustomView(frame: bounds)
32
33
    /// An Image to be used as a mask for the `customView`.
34
    public var maskingImage: UIImage? {
35
        didSet {
!
36
            setupMask()
!
37
        }
!
38
    }
39
40
    /// A transformation to apply to the `maskingImage`.
41
    public var maskTransformation: ImageMaskedViewTransformation = .asIs {
!
42
        didSet {
!
43
            guard oldValue != maskTransformation else {
!
44
                return
!
45
            }
!
46
            updateMask()
!
47
        }
!
48
    }
49
50
    private lazy var imageView = UIImageView(frame: bounds)
51
52
    /// Initializes and returns a newly allocated view object with the specified frame rectangle.
53
    /// - Parameter frame: The frame rectangle for the view, measured in points. The origin of the frame is relative
54
    ///   to the superview in which you plan to add it.
55
    public override init(frame: CGRect) {
!
56
        super.init(frame: frame)
!
57
        setupSubviews()
!
58
    }
!
59
60
    /// Returns an object initialized from data in a given unarchiver.
61
    /// - Parameter coder: An unarchiver object.
62
    public required init?(coder: NSCoder) {
!
63
        super.init(coder: coder)
!
64
        setupSubviews()
!
65
    }
!
66
67
    private func setupSubviews() {
!
68
        layoutMargins = .zero
!
69
        translatesAutoresizingMaskIntoConstraints = false
!
70
        insetsLayoutMarginsFromSafeArea = false
!
71
!
72
        addSubview(customView)
!
73
        customView.translatesAutoresizingMaskIntoConstraints = false
!
74
        NSLayoutConstraint.activate([
!
75
            customView.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
!
76
            customView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor),
!
77
            customView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
!
78
            customView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor)
!
79
        ])
!
80
    }
!
81
82
    private func setupMask() {
!
83
        guard let bubbleImage = maskingImage else {
!
84
            imageView.image = nil
!
85
            mask = nil
!
86
            return
!
87
        }
!
88
!
89
        imageView.image = bubbleImage
!
90
        mask = imageView
!
91
        updateMask()
!
92
    }
!
93
94
    private func updateMask() {
!
95
        UIView.performWithoutAnimation {
!
96
            let multiplier = effectiveUserInterfaceLayoutDirection == .leftToRight ? 1 : -1
!
97
            switch maskTransformation {
!
98
            case .flippedVertically:
!
99
                imageView.transform = CGAffineTransform(scaleX: CGFloat(multiplier * -1), y: 1)
!
100
            case .asIs:
!
101
                imageView.transform = CGAffineTransform(scaleX: CGFloat(multiplier * 1), y: 1)
!
102
            }
!
103
        }
!
104
    }
!
105
106
    /// The frame rectangle, which describes the view’s location and size in its superview’s coordinate system.
107
    public override final var frame: CGRect {
108
        didSet {
!
109
            imageView.frame = bounds
!
110
        }
!
111
    }
112
113
    /// The bounds rectangle, which describes the view’s location and size in its own coordinate system.
114
    public override final var bounds: CGRect {
115
        didSet {
!
116
            imageView.frame = bounds
!
117
        }
!
118
    }
119
120
}