在 TVOS 上的 SwiftUI 布局中苦苦挣扎

Struggling with layout in SwiftUI on TVOS

我在 TVOS 中使用 SwiftUI 有一个可用的 UI,但我不知道如何正确布局它。

目标:

  1. 全屏显示,未插入安全区域
  2. 相册图片为正方形,纵横比不变,从上到下填充
  3. 专辑和艺术家的文字布局舒适,靠近专辑封面
import SwiftUI

struct ContentView: View {
    @ObservedObject var ds = DataStore()
    
    var body: some View {
        HStack(alignment: .top) {
            if (ds.currentRoom.albumImage != nil) {
                Image(uiImage: ds.currentRoom.albumImage!)
                    .resizable()
                    .aspectRatio(contentMode: ContentMode.fit)
                    .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
                    .background(Color.black)
                
            }
            VStack(alignment: .leading, spacing: 8) {
                Text(ds.currentRoom.artist ?? "?")
                    .font(.system(.title, design: .default))
                    .bold()
                    .foregroundColor(Color.gray)
                    .padding(.top, 100)
                Text(ds.currentRoom.title ?? "?")
                    .font(.system(.headline, design: .default))
                    .foregroundColor(Color.gray)
                    .padding(.bottom, 100)
                Button(action: { print ("pressed!" )} ) {
                    Image(systemName: "playpause")
                        .font(.system(.title, design: .default))
                        .foregroundColor(Color.gray)
                        .padding(30)
                        .overlay(
                            RoundedRectangle(cornerRadius: 16)
                                .stroke(Color.green, lineWidth: 4)
                    )
                }
            }
        }
        .padding(20)
        .background(Color.black)
        .edgesIgnoringSafeArea(.all)
    }
}


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

如果有帮助,这里是相关的帮助器和数据源方法:

DataStore() class 有这个方法:

import SwiftUI
import Combine

class CurrentRoom {
    var artist: String?
    var title: String?
    var album: String?
    var albumArtURI: String?
    var absoluteAlbumArtURI: String?
    var albumImage: UIImage?
}

class DataStore: ObservableObject {
    @Published var currentRoom: CurrentRoom = CurrentRoom()
        
    init() {
        getCurrentRoom()
    }
    
    func getCurrentRoom() {
        currentRoom.artist = "Eilen Jewell"
        currentRoom.album = "Sundown over Ghost Town"
        currentRoom.title = "Half-Broke Horse"
        currentRoom.absoluteAlbumArtURI = "https://images-na.ssl-images-amazon.com/images/I/71mkKfTQD0L._SX425_.jpg"
        currentRoom.albumImage = Api().getImageDataFromURI(UrlAsString: currentRoom.absoluteAlbumArtURI)
    }
}

struct DataStore_Previews: PreviewProvider {
    static var previews: some View {
        /*@START_MENU_TOKEN@*/Text("Hello, World!")/*@END_MENU_TOKEN@*/
    }
}

最后:

class Api {
    func getImageDataFromURI(UrlAsString: String?) -> UIImage? {
        if let URI = UrlAsString {
            if URI.starts(with: "http") {
                if let url = URL(string: URI) {
                    let data = try? Data(contentsOf: url)

                    if let imageData = data {
                        return UIImage(data: imageData)
                    }
                }
            }
        }
        return nil
    }
}

目标:

您可以选择 Asperis 答案,或者,如果 ContentMode.Fit 对您很重要(因为您不想剪裁图像),只需删除图像中框架修饰符的宽度部分,然后在VStack后面加一个Spacer,剩下的会消耗掉:

struct ContentView: View {
@ObservedObject var ds = DataStore()

var body: some View {
    HStack(alignment: .top) {
            if (ds.currentRoom.albumImage != nil) {
                Image(uiImage: ds.currentRoom.albumImage!)
                    .resizable()
                    .aspectRatio(contentMode: ContentMode.fit)
                    .frame(minHeight: 0, maxHeight: .infinity)
                    .background(Color.black)
                
            }
            VStack(alignment: .leading, spacing: 8) {
                Text(ds.currentRoom.artist ?? "?")
                    .font(.system(.title, design: .default))
                    .bold()
                    .foregroundColor(Color.gray)
                    .padding(.top, 100)
                Text(ds.currentRoom.title ?? "?")
                    .font(.system(.headline, design: .default))
                    .foregroundColor(Color.gray)
                    .padding(.bottom, 100)
                Button(action: { print ("pressed!" )} ) {
                    Image(systemName: "playpause")
                        .font(.system(.title, design: .default))
                        .foregroundColor(Color.gray)
                        .padding(30)
                        .overlay(
                            RoundedRectangle(cornerRadius: 16)
                                .stroke(Color.green, lineWidth: 4)
                    )
                }
            }
            .padding(.leading, 10)
        Spacer()
        }
    .padding(20)
    .background(Color.black)
    .edgesIgnoringSafeArea(.all)
    }
}

这将产生这张图片: Correct layout