SwiftUI: как найти высоту изображения и использовать ее для установки размера кадра - PullRequest
0 голосов
/ 13 июля 2020

Итак, я уже некоторое время безуспешно пытался выяснить, как найти высоту изображения в SwiftUI. По сути, у меня есть портретные / альбомные изображения, которые я хочу отображать с высотой около 600 точек, тогда как альбомные изображения, которые я хочу отображать, используют ширину изображения, поэтому высота любая. При этом я хочу, чтобы он находился внутри GeometryReader, поскольку смещение, которое я использую, использует его, так что при прокрутке вниз изображение не прокручивается, но если вы прокручиваете вверх, оно прокручивается. Я не верю, что могу делиться изображениями, но в любом случае этого кода должно быть достаточно для его успешного запуска!

struct ContentView: View {
    
    @Environment(\.presentationMode) var mode: Binding<PresentationMode>
  
    var backButton: some View {
        Button(action: {
            withAnimation {
                self.mode.wrappedValue.dismiss()
            }
        }) {
            Image(systemName: "chevron.left.circle.fill")
                .opacity(0.8)
                .font(.system(size: 30))
                .foregroundColor(.black)
                .background(Color.gray.mask(Image(systemName: "chevron.left").offset(x: 9.4, y: 7.6)))
                
        }
    }

    var body: some View {
        ScrollView(showsIndicators: false) {
            
                GeometryReader { geometry in
                    Image("image1")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(maxWidth: UIScreen.main.bounds.width, minHeight: 350, maxHeight: 600, alignment: .top)
                        .clipped()
                        .overlay(
                            Rectangle()
                                .foregroundColor(.clear)
                                .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
                        )
                       .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

                }
                .frame(minWidth: UIScreen.main.bounds.width, maxWidth: UIScreen.main.bounds.width, minHeight: 350, idealHeight: 600, maxHeight: 600)

                //Message
                VStack(alignment: .leading) {
                    Text("\(07/22/20) - Day \(1)")
                        .font(.body)
                        .fontWeight(.light)
                        .foregroundColor(Color.gray)
                        .padding(.leading)
                    Text("what I Love)
                        .font(.title)
                        .fontWeight(.bold)
                        .padding([.leading, .bottom])

                    Text("paragraph")
                        .padding([.leading, .bottom, .trailing])
                }
   
        }
        .edgesIgnoringSafeArea(.all)
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: backButton)
    }
}

Любая помощь будет принята с благодарностью!

РЕДАКТИРОВАТЬ: Вот два изображения для справки - Извините, я не осознавал, что вы можете это сделать, хотя я должен был это догадаться!

Model Couple Holding Hands

Спагетти рамен

1 Ответ

1 голос
/ 13 июля 2020

Если я правильно понял, вам нужен размер изображения, чтобы вы могли использовать его размеры в собственном фрейме? Вы можете создать функцию, которая принимает имя изображения и возвращает желаемое изображение, одновременно задавая @State переменные для width и height, которые вы можете использовать во фрейме.

    @State var width: CGFloat = 0
    @State var height: CGFloat = 0
    
    func image(_ name: String) -> UIImage {
        let image = UIImage(named: name)!
        let width = image.size.width
        let height = image.size.height

        DispatchQueue.main.async {
            if width > height {
                // Landscape image
                // Use screen width if < than image width
                self.width = width > UIScreen.main.bounds.width ? UIScreen.main.bounds.width : width
                // Scale height
                self.height = self.width/width * height
            } else {
                // Portrait
                // Use 600 if image height > 600
                self.height = height > 600 ? 600 : height
                // Scale width
                self.width = self.height/height * width
            }
        }
        return image
    }

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

    GeometryReader { geometry in
        Image(uiImage: image("image1"))
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(width: width, height: height)
            .overlay(
                Rectangle()
                    .foregroundColor(.clear)
                    .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
            )
            .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

edit: Добавлен блок DispatchQueue.main.async как быстрое исправление для предупреждения «Изменение состояния во время обновления представления». Работает, но, вероятно, не лучший способ go об этом. Может быть, лучше поместить изображение в его собственный @State и установить его в методе представления onAppear.

edit 2: переместил изображение в свое собственное @State var, которое, как я считаю, является лучшим решением так как это сделает представление более пригодным для повторного использования, поскольку вы можете передать изображение вместо жесткого кодирования имени изображения в представлении.

    @State var image: UIImage {
        didSet {
            let width = image.size.width
            let height = image.size.height
            DispatchQueue.main.async {
                if width > height {
                    // Landscape image
                    self.width = width > UIScreen.main.bounds.width ? UIScreen.main.bounds.width : width
                    self.height = self.width/width * height
                } else {
                    
                    self.height = height > 600 ? 600 : height
                    self.width = self.height/height * width
                }
            }
        }
    }

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

    GeometryReader { geometry in
        Image(uiImage: image)
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(width: width, height: height)
            .overlay(
                Rectangle()
                    .foregroundColor(.clear)
                    .background(LinearGradient(gradient: Gradient(colors: [.clear, .white]), startPoint: .center, endPoint: .bottom))
            )
            .offset(y: geometry.frame(in: .global).minY > 0 ? -geometry.frame(in: .global).minY : 0)

Придется обновите способ объявления ContentView, передав UIImage:

ContentView(image: UIImage(named: "image0")!)
...