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

Posted

技术标签:

【中文标题】在 TVOS 上的 SwiftUI 布局中苦苦挣扎【英文标题】:Struggling with layout in SwiftUI on TVOS 【发布时间】:2020-07-10 23:39:13 【问题描述】:

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

目标:

    屏幕为全屏,未插入安全区域 相册图片为方形,保留纵横比,从上到下填充 专辑和艺术家文本布局舒适,靠近专辑封面
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()
    

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

DataStore() 类有这个方法:

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
    

目标:

【问题讨论】:

你肯定需要ContentMode.fill,否则即使图像的框架可以按照你的意愿对齐,由于不适当的纵横比,图像本身在框架内可能会在水平或垂直方向上留下空白。跨度> 【参考方案1】:

您可以使用 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

【讨论】:

以上是关于在 TVOS 上的 SwiftUI 布局中苦苦挣扎的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - tvOS 上的 AVPlayerViewController 全屏

我正在为使用 ConstraintLayout 和 Barrier 的 Android 布局而苦苦挣扎

多次使用 Android TouchDelegate 苦苦挣扎

如何在 SwiftUI 中使 UIViewRepresentable 在 tvOS 上具有焦点?

一年后,仍然在单元测试、集成测试和 E2E 测试中苦苦挣扎

在 Python 的 REST API 程序中以多进程方式调用方法而苦苦挣扎