【问题标题】:Placing background image and text/icon in a button in SwiftUI?在 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") 由于某种原因不在按钮之外!

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

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

【问题讨论】:

    标签: swiftui


    【解决方案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/… 并将其放在资产目录中
      • 抱歉,我的意思是如果远程加载背景图片会怎样。
      • 不,不是。它保存在本地。我想构建一些东西来回答你的布局问题
      • 很公平!很感激。
      猜你喜欢
      • 2014-04-09
      • 2011-03-08
      • 2015-02-07
      • 2013-10-08
      • 2013-11-28
      • 1970-01-01
      • 2014-01-13
      • 2021-05-02
      • 2018-04-18
      相关资源
      最近更新 更多