6
6
//
7
7
8
8
import SwiftUI
9
- import UIKit
10
9
import AVKit
11
10
12
11
/// Player view for running a video in loop
13
- @available ( iOS 14 . 0 , * )
14
- public struct LoopPlayerView : UIViewRepresentable {
15
-
12
+ @available ( iOS 14 . 0 , macOS 11 . 0 , tvOS 14 . 0 , * )
13
+ public struct LoopPlayerView : View {
14
+
16
15
/// Set of settings for video the player
17
- public let settings : Settings
16
+ public let settings : Settings
18
17
19
- // MARK: - Life circle
18
+ // MARK: - Life cycle
20
19
21
20
/// Player initializer
22
21
/// - Parameters:
@@ -26,17 +25,17 @@ public struct LoopPlayerView: UIViewRepresentable {
26
25
/// - eText: Error message text if file is not found
27
26
/// - eFontSize: Size of the error text
28
27
public init (
29
- fileName: String ,
30
- ext: String = " mp4 " ,
31
- gravity: AVLayerVideoGravity = . resizeAspect,
32
- eColor : Color = . accentColor,
33
- eFontSize : CGFloat = 17.0
28
+ fileName: String ,
29
+ ext: String = " mp4 " ,
30
+ gravity: AVLayerVideoGravity = . resizeAspect,
31
+ eColor: Color = . accentColor,
32
+ eFontSize: CGFloat = 17.0
34
33
) {
35
- settings = Settings {
34
+ settings = Settings {
36
35
FileName ( fileName)
37
36
Ext ( ext)
38
37
Gravity ( gravity)
39
- ErrorGroup {
38
+ ErrorGroup {
40
39
EColor ( eColor)
41
40
EFontSize ( eFontSize)
42
41
}
@@ -45,42 +44,55 @@ public struct LoopPlayerView: UIViewRepresentable {
45
44
46
45
/// Player initializer in a declarative way
47
46
/// - Parameter settings: Set of settings
48
- public init ( _ settings : ( ) -> Settings ) {
47
+ public init ( _ settings: ( ) -> Settings ) {
49
48
self . settings = settings ( )
50
49
}
51
-
50
+
52
51
// MARK: - API
53
52
54
- /// Inherited from UIViewRepresentable
55
- public func updateUIView( _ uiView: UIView , context: UIViewRepresentableContext < LoopPlayerView > ) {
53
+ public var body : some View {
54
+ #if os(iOS) || os(tvOS)
55
+ LoopPlayerViewRepresentableIOS ( settings: settings)
56
+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
57
+ #elseif os(macOS)
58
+ LoopPlayerViewRepresentableMacOS ( settings: settings)
59
+ . frame ( maxWidth: . infinity, maxHeight: . infinity)
60
+ #endif
56
61
}
62
+ }
57
63
58
- /// - Parameter context: Contains details about the current state of the system
59
- /// - Returns: View
60
- public func makeUIView( context: Context ) -> UIView {
61
-
64
+ // MARK: - Representable for iOS and tvOS
65
+
66
+ #if os(iOS) || os(tvOS)
67
+ @available ( iOS 14 . 0 , tvOS 14 . 0 , * )
68
+ struct LoopPlayerViewRepresentableIOS : UIViewRepresentable {
69
+
70
+ let settings : Settings
71
+
72
+ func makeUIView( context: Context ) -> UIView {
62
73
let name = settings. name
63
74
let ext = settings. ext
64
75
let gravity = settings. gravity
65
76
let color = settings. errorColor
66
77
let fontSize = settings. errorFontSize
67
78
68
- guard settings. areUnique else {
69
- return errorTpl ( . settingsNotUnique, color, fontSize)
79
+ guard settings. areUnique else {
80
+ return errorTpliOS ( . settingsNotUnique, color, fontSize)
70
81
}
71
82
72
- guard let view = LoopingPlayerUIView ( name, width: ext, gravity: gravity) else {
73
- return errorTpl ( . fileNotFound( name) , color, fontSize)
83
+ guard let view = LoopingPlayerUIView ( name, width: ext, gravity: gravity) else {
84
+ return errorTpliOS ( . fileNotFound( name) , color, fontSize)
85
+ }
86
+ return view
74
87
}
75
- return view
88
+
89
+ func updateUIView( _ uiView: UIView , context: Context ) {
76
90
}
77
91
}
78
92
93
+ // MARK: - Helpers for iOS and tvOS
79
94
80
- // MARK: - Helpers
81
-
82
- /// https://stackoverflow.com/questions/12591192/center-text-vertically-in-a-uitextview
83
- fileprivate class ErrorMsgTextView : UITextView {
95
+ fileprivate class ErrorMsgTextViewIOS : UITextView {
84
96
85
97
override var contentSize : CGSize {
86
98
didSet {
@@ -91,13 +103,108 @@ fileprivate class ErrorMsgTextView: UITextView {
91
103
}
92
104
}
93
105
94
- /// - Returns: Error view
95
- fileprivate func errorTpl( _ error : VPErrors , _ color : Color , _ fontSize: CGFloat ) -> ErrorMsgTextView {
96
- let textView = ErrorMsgTextView ( )
106
+ fileprivate func errorTpliOS( _ error: VPErrors , _ color: Color , _ fontSize: CGFloat ) -> UIView {
107
+ let textView = ErrorMsgTextViewIOS ( )
97
108
textView. backgroundColor = . clear
98
109
textView. text = error. description
99
110
textView. textAlignment = . center
100
111
textView. font = UIFont . systemFont ( ofSize: fontSize)
101
112
textView. textColor = UIColor ( color)
102
113
return textView
103
114
}
115
+
116
+ #endif
117
+
118
+ // MARK: - Representable for macOS
119
+
120
+ #if os(macOS)
121
+ @available ( macOS 11 . 0 , * )
122
+ struct LoopPlayerViewRepresentableMacOS : NSViewRepresentable {
123
+
124
+ let settings : Settings
125
+
126
+ func makeNSView( context: Context ) -> NSView {
127
+ let name = settings. name
128
+ let ext = settings. ext
129
+ let gravity = settings. gravity
130
+ let color = settings. errorColor
131
+ let fontSize = settings. errorFontSize
132
+
133
+ guard settings. areUnique else {
134
+ return errorTplmacOS ( . settingsNotUnique, color, fontSize)
135
+ }
136
+
137
+ guard let view = LoopingPlayerNSView ( name, width: ext, gravity: gravity) else {
138
+ return errorTplmacOS ( . fileNotFound( name) , color, fontSize)
139
+ }
140
+ return view
141
+ }
142
+
143
+ func updateNSView( _ nsView: NSView , context: Context ) {
144
+ }
145
+ }
146
+
147
+ // MARK: - Helpers for macOS
148
+
149
+ fileprivate class ErrorMsgTextViewMacOS : NSTextView {
150
+
151
+ override var intrinsicContentSize : NSSize {
152
+ return NSSize ( width: NSView . noIntrinsicMetric, height: NSView . noIntrinsicMetric)
153
+ }
154
+
155
+ override func viewDidMoveToSuperview( ) {
156
+ super. viewDidMoveToSuperview ( )
157
+ guard let superview = superview else { return }
158
+
159
+ translatesAutoresizingMaskIntoConstraints = false
160
+
161
+ NSLayoutConstraint . activate ( [
162
+ leadingAnchor. constraint ( equalTo: superview. leadingAnchor, constant: 10 ) ,
163
+ trailingAnchor. constraint ( equalTo: superview. trailingAnchor, constant: - 10 ) ,
164
+ topAnchor. constraint ( equalTo: superview. topAnchor) ,
165
+ bottomAnchor. constraint ( equalTo: superview. bottomAnchor)
166
+ ] )
167
+ }
168
+
169
+ override func layout( ) {
170
+ super. layout ( )
171
+
172
+ guard let layoutManager = layoutManager, let textContainer = textContainer else {
173
+ return
174
+ }
175
+
176
+ let textHeight = layoutManager. usedRect ( for: textContainer) . size. height
177
+ let containerHeight = bounds. size. height
178
+ let verticalInset = max ( 0 , ( containerHeight - textHeight) / 2 )
179
+
180
+ textContainerInset = NSSize ( width: 0 , height: verticalInset)
181
+ }
182
+ }
183
+
184
+ fileprivate func errorTplmacOS( _ error: VPErrors , _ color: Color , _ fontSize: CGFloat ) -> NSView {
185
+ let textView = ErrorMsgTextViewMacOS ( )
186
+ textView. isEditable = false
187
+ textView. isSelectable = false
188
+ textView. drawsBackground = false
189
+ textView. string = error. description
190
+ textView. alignment = . center
191
+ textView. font = NSFont . systemFont ( ofSize: fontSize)
192
+ textView. textColor = NSColor ( color)
193
+
194
+ let containerView = NSView ( )
195
+ containerView. addSubview ( textView)
196
+
197
+ textView. translatesAutoresizingMaskIntoConstraints = false
198
+
199
+ // Center textView in containerView with padding
200
+ NSLayoutConstraint . activate ( [
201
+ textView. centerXAnchor. constraint ( equalTo: containerView. centerXAnchor) ,
202
+ textView. centerYAnchor. constraint ( equalTo: containerView. centerYAnchor) ,
203
+ textView. leadingAnchor. constraint ( greaterThanOrEqualTo: containerView. leadingAnchor, constant: 10 ) ,
204
+ textView. trailingAnchor. constraint ( lessThanOrEqualTo: containerView. trailingAnchor, constant: - 10 )
205
+ ] )
206
+
207
+ return containerView
208
+ }
209
+
210
+ #endif
0 commit comments