Search code examples
swiftswiftuiuiimageswiftui-sharelink

Cannot generate image which is then shared


I'm coding in swiftUI, and I'm trying to create a button that first generates a new image and then opens a ShareSheet where the new image can be shared. However, with the code below, only the description is shared. Can someone please help me with what is wrong?

import SwiftUI
import UIKit


struct ShareSheetViewTESTER: UIViewControllerRepresentable {
    var imageToShare: UIImage
    var descriptionToShare: String

    
    var itemsToShare: [Any] {
        return [imageToShare, descriptionToShare]
    }

    func makeUIViewController(context: Context) -> UIActivityViewController {
        let activityVC = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
        return activityVC
    }

    func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {
        
    }
}

struct FriendsView: View {
    @State private var showShareSheet = false
    @State private var image = UIImage(named: "Kort")!
    @State private var description = "Some text here"
    @State private var generatedImage: UIImage?

    var body: some View {
        VStack {
            Button(action: {
                generateImage()
                self.showShareSheet = true
            }) {
                Image(systemName: "square.and.arrow.up")
                    .frame(width: 25, height: 25)
            }
            .sheet(isPresented: $showShareSheet) {
                ShareSheetViewTESTER(imageToShare: generatedImage ?? self.image, descriptionToShare: self.description)
                
            }
        }
    }
    func generateImage() {
        if let imageFromBundle = UIImage(named: "Kort") {
            let textToOverlay = "Text to be put on the photo"
            
            let imageRenderer = UIGraphicsImageRenderer(size: imageFromBundle.size)
            let generatedImage = imageRenderer.image { context in
                imageFromBundle.draw(at: .zero)
                
                let paragraphStyle = NSMutableParagraphStyle()
                paragraphStyle.alignment = .center
                paragraphStyle.lineBreakMode = .byWordWrapping
                
                let attributes: [NSAttributedString.Key: Any] = [
                    .font: UIFont.systemFont(ofSize: 20),
                    .foregroundColor: UIColor.black,
                    .paragraphStyle: paragraphStyle
                ]
                
                let textNSString = textToOverlay as NSString
                let textSize = textNSString.boundingRect(with: CGSize(width: imageFromBundle.size.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: attributes, context: nil).size
                
                let textRect = CGRect(x: 0, y: 170, width: imageFromBundle.size.width, height: textSize.height)
                
                textNSString.draw(in: textRect, withAttributes: attributes)
            }
            
            self.generatedImage = generatedImage
            
        }
    }
}

struct FriendsView_Previews: PreviewProvider {
    static var previews: some View {
        FriendsView()
    }
}

I have tried several other solutions, but i can't get anything to work. I would be realy happy if someone can help me out here.


Solution

  • It appears that the image and description are being shared correctly (e.g., when saving to files in SwiftUI's preview), but the preview in the share sheet does not include the sharing image. This issue arises due to the way UIActivityViewController manages the presentation of shared items. To ensure the shared content is displayed accurately in the share sheet preview, a custom class conforming to the UIActivityItemSource protocol can be implemented. This approach offers enhanced control over the shared content and its metadata.

    Here's a refined solution:

    import UIKit
    import LinkPresentation
    
    // Custom class representing the item to be shared
    final class ActivityItem: NSObject {
        let name: String
        let image: UIImage
    
        init(name: String, image: UIImage) {
            self.name = name
            self.image = image
        }
    }
    
    // Conforming to UIActivityItemSource for customized sharing
    extension ActivityItem: UIActivityItemSource {
        func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
            return image
        }
    
        func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
            return image
        }
    
        // Providing metadata for link presentation
        func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
            let metaData = LPLinkMetadata()
            metaData.title = name
            metaData.imageProvider = NSItemProvider(object: image)
            return metaData
        }
    }
    

    Adjust your itemsToShare property in ShareSheetViewTESTER to include an instance of ActivityItem:

    var itemsToShare: [Any] {
       [ActivityItem(name: descriptionToShare, image: imageToShare), descriptionToShare]
    }
    

    This modification ensures that the UIActivityViewController receives the image in a format that it can correctly process for both sharing and previewing in the share sheet, while maintaining the correct sharing functionality as observed in file-saving scenarios.