在 SwiftUI 的按钮中放置背景图像和文本/图标?

Posted

技术标签:

【中文标题】在 SwiftUI 的按钮中放置背景图像和文本/图标?【英文标题】:Placing background image and text/icon in a button in SwiftUI? 【发布时间】:2021-02-03 16:27:40 【问题描述】:

我正在尝试在 SwiftUI 中实现这样的目标:

我可以创建带有圆角的按钮,也可以将文本放在它的中心,仅此而已。

我需要能够在顶部放置背景图像和页脚标题/文本和图标!

这是我目前所拥有的:

                            Button(action: 
                                    //self.dismiss?()
            
                                    
                                )
                                
                               

                                    HStack(spacing: 10) 
                                        Image(systemName: "pencil").renderingMode(.template)
                                        Text(audio.name)
                                            //.font(.headline)
                                            .frame(width: 160, height: 200)
                                        
                                            .background(Color.gray)
                                            .addBorder(Color.white, width: 1, cornerRadius: 10)
                                    
                                    
                    
                                    
                                

当我尝试我的代码时,我得到一个圆角按钮,中间有文字,但 Image(systemName: "pencil") 由于某种原因不在按钮之外!

有人可以指导我如何实现这一目标吗?

值得一提的是,背景图片来自远程服务器。

【问题讨论】:

【参考方案1】:

首先,为了处理加载图像的后端,创建一个处理状态和网络的 Observable 对象。

import Combine



class ImageManager: ObservableObject 

    // Property that fires an update onto the interface when it has any changes to its value
    @Published var image: UIImage? = nil

    init(link: String) 
        // Standard way of loading data from a link.
        guard let url = URL(string: link) else  return 
        let task = URLSession.shared.dataTask(with: url)  (data, response, error) in
            if let error = error 
                // handle the error here !
                print(error)
             else if let data = data 
                // Never forget to update on the main thread ! ?
                DispatchQueue.main.async 
                    self.image = UIImage(data: data)
                
            
        
        task.resume()
     

其次,构建处理与 Observable 对象通信的 Image 视图,如下所示:

struct ImageLoader: View 

     // The property wrapper '@ObservedObject' makes our ImageLoader change its interface according to updates received from our ImageManager
     @ObservedObject private var manager: ImageManager

     init(link: String) 
         manager = ImageManager(link: link)
     

     var body: some View 
         Image(uiImage: manager.image ?? UIImage())
            .resizable()
            .aspectRatio(contentMode: .fill)
            // Make the view greyish as long there is no image loaded to present
            .redacted(reason: manager.image == nil ? .placeholder:[])
    

第三,构建最终视图。您可以使用叠加层在卡片顶部添加视图以及

.frame(maxWidth:, maxHeight, 对齐方式:)

在没有太多额外视图的情况下有效地构建您的叠加视图。 ?

struct MovieCard: View 

    @State var thumbnailLink = "https://media.senscritique.com/media/000010045541/source_big/Tomb_Raider.png"

     var body: some View 
        ImageLoader(link: thumbnailLink)
            .frame(width: 200, height: 200)
            // Overlay is a very simple way to add content on top of your view ! ?
            .overlay(
                 VStack 
                     // Using the system heart icon see https://developer.apple.com/sf-symbols/
                     Image(systemName: "suit.heart.fill")
                        .font(.title3)
                        .padding()
                        // Using maxWidth, maxHeight and alignement is usefull to not use additionnal views in your code (such as spacers, stacks, etc…)
                        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                        .foregroundColor(.red)
                
                     Text("Movie title")
                        .font(Font.body.bold())
                        .foregroundColor(.white)
                        .padding()
                        .frame(maxWidth: .infinity)
                        .background(Color.red.opacity(0.7))
                
            )
            .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
            .shadow(color: Color.black.opacity(0.3), radius: 6, x: 0, y: 3)
            .onTapGesture 
                // Handle the action when the card is touched
            
        
    

【讨论】:

这也很棒。如果背景图片在远程服务器上怎么办? 只需构建一个简单的 ImageLoader 对象,它关心 URLSession、加载状态等……然后将 Image("Tomb_Raider") 替换为您的 ImageLoader 对象。 ? 我会在我的答案中添加更多内容,以便提供适当的详细信息? 来了!我在那里详细一点。希望它可以帮助您更好地理解一种处理方式?玩得开心!【参考方案2】:

将框架和背景修饰符移动到 hstack:

            HStack 
                        Image(systemName: "pencil").renderingMode(.template)
                        Spacer()
                        Text("title goes here")
                        Spacer()
                .frame(width: 160, height: 200)
                
                .background(Color.gray)
            

您还可以在ImageText 之间添加Spacer(),在Text 之后添加Spacer(),以正确对齐所有内容

【讨论】:

【参考方案3】:

有很多元素可以让它正确,所以我在下面的相关位置放置了 cmets。

Button(action:  ) 
    Image("Tomb_Raider")
        //declare your image as resizable, otherwise it will keep its original size
        .resizable()
        //declare the frame of the image (i.e. the size you want it to resize to)
        .frame(width: 160, height: 200)
        // place the red rectangle with text in an overlay
        // as opposed to HStack where elements are side by side
        // here the image will be placed under the rest
        .overlay(
            //This HStack places the text in the middle with some padding
            HStack 
                Spacer()
                Text("Title goes Here")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding(.vertical)
                 Spacer()
               
                //set the background of the text to be semitransparent red
                .background(Color.red.opacity(0.6)),
                //this defines the alignment of the overlay
                alignment: .bottom)
        //clip everything to get rounded corners
        .clipShape(RoundedRectangle(cornerRadius: 10))
    
        

这就是最终结果的样子:

【讨论】:

这太棒了。感谢cmets。如果背景图片在远程服务器上怎么办? 没问题。我已经从这里下载了图片:upload.wikimedia.org/wikipedia/en/thumb/2/29/… 并将其放在资产目录中 抱歉,我的意思是如果远程加载背景图片会怎样。 不,不是。它保存在本地。我想构建一些东西来回答你的布局问题 很公平!很感激。

以上是关于在 SwiftUI 的按钮中放置背景图像和文本/图标?的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中放置在导航堆栈中时滚动视图的背景颜色

我可以在 SwiftUI 的单个列表部分中放置多个文本行吗?

是否可以在 macOS 状态栏中放置矢量(或字体)图标?

在 iOS LaunchScreen.storyboard 中放置图像会完全禁用启动屏幕

将文本和按钮定位在图像上的特定位置

在导航控制器中放置后退按钮