Как я могу создать цвет фона с непрозрачностью на Листе просмотра?

#swiftui #swift5

#swiftui #swift5

Вопрос:

В настоящее время я разрабатываю приложение с использованием SwiftUI.

Я ищу какой-нибудь способ создать цвет фона с непрозрачностью на листе.

есть ли какой-либо способ сделать это?


Я пытался сделать это с помощью приведенного ниже кода, я могу изменить цвет с помощью свойства непрозрачности, но я не вижу текст (лист) в представлении листа…

 import SwiftUI

struct ContentView: View {
    
    @State var isSheet = false
    
    var body: some View {
       
        Button(action: {self.isSheet.toggle()}) {
            Text("Sheet")
        }.sheet(isPresented: $isSheet){
            Color.yellow.opacity(0.5)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}  
  

Xcode: версия 11.7

Swift: Swift 5

Ответ №1:

Вы не можете сделать это с помощью стандартного sheet official API (потому что каждое представление контроллера хостинга по умолчанию непрозрачно), поэтому вы можете либо создать пользовательский вид листа (с любыми необходимыми функциями), либо использовать обходной путь во время выполнения, чтобы найти это представление и установить для него значение очистить фон. Как показано ниже (только для демонстрации)

ДЕМОНСТРАЦИЯ

 struct DemoView: View {

    @State var isSheet = false

    var body: some View {

        Button(action: {self.isSheet.toggle()}) {
            Text("Sheet")
        }.sheet(isPresented: $isSheet){
            Color.yellow.opacity(0.5)
                .background(BackgroundClearView())
        }
    }
}

struct BackgroundClearView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}
  

Комментарии:

1. Если мы добавим «.ignoresSafeArea()» после «.background(BackgroundClearView())», вид будет именно таким, как мы хотим.

2. Я обнаружил, что, по крайней мере, в iOS 16, иногда была очень короткая задержка в настройке цвета фона, поэтому он мерцал для меня при появлении представления. Вместо использования асинхронной отправки, замена UIView для этого работает лучше: class SuperviewRecolourView: UIView { override func layoutSubviews() { guard let parentView = superview?.superview else { print("ERROR: Failed to get parent view to make it clear") return } parentView.backgroundColor = .clear } } (красивый макет комментария: D)

3. Это не работает, когда лист является NavigationView или NavigationStack. Кто-нибудь знает, как это исправить?

Ответ №2:

Используя ПОТРЯСАЮЩИЙ ответ от @Asperi, который я пытался найти весь день, я создал простой модификатор вида, который теперь можно применять внутри модального представления .sheet или .fullScreenCover и обеспечивает прозрачный фон. Затем вы можете установить модификатор рамки для содержимого по мере необходимости, чтобы оно соответствовало экрану, при этом пользователю не нужно знать, что модальный размер не соответствует пользовательскому.

 import SwiftUI

struct ClearBackgroundView: UIViewRepresentable {
    func makeUIView(context: Context) -> some UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }
    func updateUIView(_ uiView: UIViewType, context: Context) {
    }
}

struct ClearBackgroundViewModifier: ViewModifier {
    
    func body(content: Content) -> some View {
        content
            .background(ClearBackgroundView())
    }
}

extension View {
    func clearModalBackground()->some View {
        self.modifier(ClearBackgroundViewModifier())
    }
}
  

Использование:

 .sheet(isPresented: $isPresented) {
            ContentToDisplay()
            .frame(width: 300, height: 400)
            .clearModalBackground()
    }
  

Комментарии:

1. Я обнаружил, что, по крайней мере, в iOS 16, иногда была очень короткая задержка в настройке цвета фона, поэтому он мерцал для меня при появлении представления. Вместо использования асинхронной отправки, замена UIView для этого работает лучше: class SuperviewRecolourView: UIView { override func layoutSubviews() { guard let parentView = superview?.superview else { print("ERROR: Failed to get parent view to make it clear") return } parentView.backgroundColor = .clear } } (красивый макет комментария: D)

2. Я не тестировал это на устройстве iOS 16, поэтому я ценю внимание!

Ответ №3:

С iOS 16.4 должна быть возможность использовать .presentationBackground(_ style: S) с цветами и размытиями (типы материалов). Если вы хотите сделать прозрачный фон только для одного листа:

 struct ContentView: View {    
    @State private var isPresented = false
    var body: some View {
        Button(action: {
            isPresented.toggle()
        }, label: {
            Label("Sheet", systemImage: "list.bullet.rectangle.portrait")
        })
        .sheet(isPresented: $isPresented) {
            Text("Detail")
                .presentationBackground(.clear)
        }
    }
}
  

Или вы можете использовать .presentationBackground() для наследования стиля фона от presenter.

 struct ContentView: View {
    @State private var isPresented = false
    var body: some View {
        Button(action: {
            isPresented.toggle()
        }, label: {
            Label("Sheet", systemImage: "list.bullet.rectangle.portrait")
        })
        .sheet(isPresented: $isPresented) {
            Text("Detail")
                .presentationBackground()
        }
        .backgroundStyle(Color("myThemeBackground"))
    }
}
  

Ответ №4:

Для тех, кто считает, что полагаться на порядок сообщений в DispatchQueue не очень хорошая идея (на самом деле это не так), вот решение, которое основывается на переопределении didMoveToSuperview() .

Это все еще не на 100% доказательство будущего, поскольку мы модифицируем superview в superview, и нет гарантии, что он будет доступен в момент добавления нашего собственного представления в иерархию, но я думаю, что это все же лучше, чем решение DispatchQueue, поскольку представления обычно добавляются сверху вниз, т. Е. superview в superview, скорее всего, будет доступен при didMoveToSuperview() вызове.

 struct DemoView: View {

    @State var isSheet = false

    var body: some View {

        Button(action: {self.isSheet.toggle()}) {
            Text("Sheet")
        }.sheet(isPresented: $isSheet){
            Color.yellow.opacity(0.5)
                .background(BackgroundClearView())
        }
    }
}

struct BackgroundClearView: UIViewRepresentable {

    private class View: UIView {
        override func didMoveToSuperview() {
            super.didMoveToSuperview()
            superview?.superview?.backgroundColor = .clear
        }
    }

    func makeUIView(context: Context) -> UIView {
        return View()
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

  

Ответ №5:

Хотя предоставленные решения действительно работают, они сохранят фон прозрачным и для других листов, когда они будут заменены.

Итак, необходимо восстановить цвет фона dismantleUIView следующим образом:

 struct TransparentBackground: UIViewRepresentable {
    @MainActor
    private static var backgroundColor: UIColor?

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        Task {
            Self.backgroundColor = view.superview?.superview?.backgroundColor
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

    static func dismantleUIView(_ uiView: UIView, coordinator: ()) {
        uiView.superview?.superview?.backgroundColor = Self.backgroundColor
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}
  

Обратите внимание, что я использовал новые функции параллелизма в Swift.

Комментарии:

1. Это здорово и отлично работает и в iOS 16!

Ответ №6:

Поместите это в Modal.swift :

 import SwiftUI

// View modifiers for modals: .sheet, .fullScreenCover

struct ModalColorView: UIViewRepresentable {
    
    let color: UIColor
    
    func makeUIView(context: Context) -> some UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = color
        }
        return view
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {}
}

struct ModalColorViewModifier: ViewModifier {
    
    let color: UIColor
    
    func body(content: Content) -> some View {
        content
            .background(ModalColorView(color: color))
    }
}

extension View {
    /// Set transparent or custom color for a modal background (.screen, .fullScreenCover)
    func modalColor(_ color: UIColor = .clear) -> some View {
        self.modifier(ModalColorViewModifier(color: color))
    }
}
  

Используйте вот так:

 .sheet(isPresented: $show) {
  YourModalView(isPresented: $show)
    // Zero args default is transparent (.clear)
    //.modalColor()
    // ..or specify a UIColor of choice.
    .modalColor(UIColor(white: 0.2, alpha: 0.3))
  }
}
  

Комментарии:

1. Я обнаружил, что, по крайней мере, в iOS 16, иногда была очень короткая задержка в настройке цвета фона, поэтому он мерцал для меня при появлении представления. Вместо использования асинхронной отправки, замена UIView для этого работает лучше: class SuperviewRecolourView: UIView { override func layoutSubviews() { guard let parentView = superview?.superview else { print("ERROR: Failed to get parent view to make it clear") return } parentView.backgroundColor = .clear } } (красивый макет комментария: D)

2. @CMash хаха да, великолепная подсветка синтаксиса в разделе комментариев, не так ли! Спасибо за это понимание, оно полезно. Я опубликовал это до iOS 16 и теперь собираюсь использовать что-то подобное для iOS 16, так что ваш комментарий своевременен и оценен.