Я хочу создать полноэкранный предварительный просмотр камеры, где середина изображения представляет собой прямоугольник. Все внутри прямоугольника должно быть opaque
, а все снаружи должно быть semi-transparent
:
Сплошные линии обозначают opaque
части предварительного просмотра (серый прямоугольник), а пунктирные линии — semi-transparent
части.
В настоящее время я использую стандарт AVCaptureVideoPreviewLayer
, но не вижу способа применить какой-либо alpha
к выводу, не говоря уже о применении альфа только к некоторым областям. Это возможно?
ОБНОВЛЯТЬ:
Работа МРЭ:
import SwiftUI
import AVFoundation
class CameraViewController: UIViewController {
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
override func viewDidLoad() {
super.viewDidLoad()
captureSession = AVCaptureSession()
captureSession.sessionPreset = .photo
guard let backCamera = AVCaptureDevice.default(for: .video) else { return }
do {
let input = try AVCaptureDeviceInput(device: backCamera)
if captureSession.canAddInput(input) {
captureSession.addInput(input)
}
} catch {
print("Error: Unable to initialize back camera: \(error.localizedDescription)")
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.videoGravity = .resizeAspectFill
previewLayer.frame = view.layer.bounds
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
}
struct CameraView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> CameraViewController {
return CameraViewController()
}
func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ZStack {
CameraView()
.edgesIgnoringSafeArea(.all)
}
}
}
Я обнаружил, что AVCaptureVideoPreviewLayer
имеет свойство opacity, которое можно настроить, чтобы сделать предварительный просмотр прозрачным, но до сих пор не вижу способа сделать непрозрачный прямоугольник.
Простите, если это требует настройки, но я играю с этим . Я просто добавил self.previewLayer.opacity = 0.5
после этой строки и Text("BACKGROUND")
перед предварительным просмотром в исполняемом демо-файле. Кажется, это делает его прозрачным (см. мое обновление).
Также нашел этот пост, но ему уже десять лет.
Я попробовал библиотеку Aespa, о которой вы упомянули выше. Поэтому я воспользуюсь этим проектом, чтобы дать вам решение. Весь экран представляет собой AVCaptureVideoPreviewLayer
. Для достижения желаемого пользовательского интерфейса вам необходимо:
AVCaptureVideoPreviewLayer
в представление вместо представления контроллера, как это было в библиотеке.На этом этапе ничего особенного, откройте файл Preview
, затем объявите собственный контроллер, скажем PreviewController
.
final class PreviewController: UIViewController {
let previewContainer = UIView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(previewContainer)
previewContainer.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
transparentView.topAnchor.constraint(equalTo: view.topAnchor),
transparentView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
transparentView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
transparentView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
struct Preview: UIViewControllerRepresentable {
func updateUIViewController(_ uiViewController: PreviewController, context: Context) {
...
if previewLayer.superlayer == nil {
uiViewController.previewContainer.layer.addSublayer(previewLayer)
}
}
}
cameraOverlayView
над слоем предварительного просмотра на шаге 1. Представьте, что красный вид в центре — это ваша opaque view
, другой — semi-transparent
части.final class CameraOverlayView: UIView {
private var container: UIStackView = {
let v = UIStackView()
v.axis = .vertical
return v
}()
private var top = UIView()
private let bottom = UIView()
private var centerContent: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.alignment = .fill
return v
}()
private let focusView = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
addSubview(container)
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
container.topAnchor.constraint(equalTo: self.topAnchor),
container.leadingAnchor.constraint(equalTo: self.leadingAnchor),
container.trailingAnchor.constraint(equalTo: self.trailingAnchor),
container.bottomAnchor.constraint(equalTo: self.bottomAnchor),
])
top.backgroundColor = .black.withAlphaComponent(0.8)
top.heightAnchor.constraint(equalToConstant: 200).isActive = true
bottom.backgroundColor = .black.withAlphaComponent(0.8)
container.addArrangedSubview(top)
container.addArrangedSubview(centerContent)
container.addArrangedSubview(bottom)
let padding = (UIScreen.main.bounds.width - 300) / 2
let leading = UIView()
leading.backgroundColor = .black.withAlphaComponent(0.8)
leading.widthAnchor.constraint(equalToConstant: padding).isActive = true
let trailing = UIView()
trailing.backgroundColor = .black.withAlphaComponent(0.8)
trailing.widthAnchor.constraint(equalToConstant: padding).isActive = true
centerContent.addArrangedSubview(leading)
centerContent.addArrangedSubview(focusView)
centerContent.addArrangedSubview(trailing)
focusView.widthAnchor.constraint(equalToConstant: 300).isActive = true
focusView.heightAnchor.constraint(equalToConstant: 400).isActive = true
}
}
Я исправил константы. Вы можете изменить их позже или ввести через параметр/функцию.
Теперь добавьте представление в PreviewController
и убедитесь, что оно находится выше previewContainer
.
final class PreviewController: UIViewController {
private var cameraOverlayView: CameraOverlayView!
override func viewDidLoad() {
super.viewDidLoad()
...
cameraOverlayView = CameraOverlayView(frame: .zero)
view.addSubview(cameraOverlayView)
cameraOverlayView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
cameraOverlayView.topAnchor.constraint(equalTo: view.topAnchor),
cameraOverlayView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
cameraOverlayView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
cameraOverlayView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
Выход:
Я пришел к аналогичным результатам: ``` .mask( Rectangle() .ignoresSafeArea() .opacity(0.5) .overlay( Rectangle() .frame(width: 200, height: 300) .opacity(1) ) ) ``` По сути, это модификатор mask
для viewModel.preview
.
О, так какова твоя главная цель? Вы сами отвечаете на вопрос? Я думал, вы должны были поддерживать более низкую версию?
Не могли бы вы попробовать и сообщить мне, что вы думаете? Трудно судить о последствиях
Я полностью ценю совет. У меня он заработал только сейчас, но хорошо, что он поддерживает более ранние версии.
С моей точки зрения, ваше решение тоже сработало хорошо, скажем, это своего рода версия SwiftUI, а мое снова возвращается к UIKit.
Я обновил пост, добавив MRE для будущих ссылок. Приношу извинения, вам пришлось возиться с Aespa. Когда я писал этот пост, у меня не было MRE. Классная вегета на заднем плане, хотя
Или, может быть, это Гоку...
Хаха, ты это заметил xD. В любом случае, удачного кодирования.
что ты уже испробовал? Не могли бы вы показать код?