Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] Custom element with custom view #304

Open
paulocoutinhox opened this issue Jul 11, 2024 · 1 comment
Open

[question] Custom element with custom view #304

paulocoutinhox opened this issue Jul 11, 2024 · 1 comment

Comments

@paulocoutinhox
Copy link

Hello,

I need a lib for Swift markdown parser/display where when interpreting a link where the href is "ubook-app://product/123", a custom view is placed inside that displays a loading, then loads the product by the link id (ex: 123), and then show the product cover, title and two buttons (read/listen) within the view.

Can you tell me how to do this with this component?

Thanks.

@paulocoutinhox
Copy link
Author

I solve the problem, but i still need click on buttons and it is not clickable, can anyone help me?

import UIKit
import Down

class MainViewController: UIViewController {

    private var textView: UITextView!
    private let exampleMarkdown = """
    Aqui está um link para um produto: [produto](ubook-app://product/123).
    Aqui está um link para um outro produto: [produto](ubook-app://product/456) que também é um produto bom.
    """

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupTextView()
        parseMarkdown()
    }

    private func setupTextView() {
        textView = UITextView()
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isEditable = false
        textView.backgroundColor = .white
        textView.textColor = .black
        textView.isScrollEnabled = true
        textView.textContainerInset = .zero
        textView.textContainer.lineFragmentPadding = 0
        textView.delegate = self
        textView.isUserInteractionEnabled = true
        textView.isSelectable = false
        view.addSubview(textView)

        NSLayoutConstraint.activate([
            textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
        ])
    }

    private func parseMarkdown() {
        let down = Down(markdownString: exampleMarkdown)
        if let attributedString = try? down.toAttributedString() {
            let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
            let fullRange = NSRange(location: 0, length: attributedString.length)

            mutableAttributedString.enumerateAttributes(in: fullRange, options: []) { attributes, range, _ in
                if let link = attributes[.link] as? URL, link.scheme == "ubook-app", link.host == "product" {
                    let productId = link.lastPathComponent
                    let attachment = NSTextAttachment()
                    let productView = createProductView(productId: productId)
                    attachment.image = imageFromView(view: productView)

                    let attachmentString = NSAttributedString(attachment: attachment)
                    mutableAttributedString.replaceCharacters(in: range, with: attachmentString)

                    self.textView.attributedText = mutableAttributedString

                    // Carregar os dados do produto de forma assíncrona
                    self.loadProductData(productId: productId) { product in
                        DispatchQueue.main.async {
                            self.updateProductView(productView: productView, product: product)
                            attachment.image = self.imageFromView(view: productView)
                            self.textView.attributedText = mutableAttributedString // Atualizar o texto após carregar o produto
                            self.textView.layoutIfNeeded() // Garantir que a UI seja atualizada
                        }
                    }
                }
            }
        }
    }

    private func createProductView(productId: String) -> UIView {
        let productView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        productView.backgroundColor = .red
        productView.isUserInteractionEnabled = true
        
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(productViewTapped(_:)))
        productView.addGestureRecognizer(tapGesture)

        let loadingView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        loadingView.backgroundColor = .green
        loadingView.translatesAutoresizingMaskIntoConstraints = false
        loadingView.tag = 1
        productView.addSubview(loadingView)

        let activityIndicator = UIActivityIndicatorView(style: .large)
        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
        activityIndicator.startAnimating()
        loadingView.addSubview(activityIndicator)

        NSLayoutConstraint.activate([
            activityIndicator.centerXAnchor.constraint(equalTo: loadingView.centerXAnchor),
            activityIndicator.centerYAnchor.constraint(equalTo: loadingView.centerYAnchor),
            loadingView.topAnchor.constraint(equalTo: productView.topAnchor),
            loadingView.leadingAnchor.constraint(equalTo: productView.leadingAnchor),
            loadingView.trailingAnchor.constraint(equalTo: productView.trailingAnchor),
            loadingView.bottomAnchor.constraint(equalTo: productView.bottomAnchor)
        ])

        let productContentView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 80))
        productContentView.backgroundColor = .blue
        productContentView.translatesAutoresizingMaskIntoConstraints = false
        productContentView.isHidden = true
        productContentView.tag = 2
        productView.addSubview(productContentView)

        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.tag = 3
        productContentView.addSubview(imageView)

        let titleLabel = UILabel()
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.textColor = .white
        titleLabel.tag = 4
        productContentView.addSubview(titleLabel)

        let listenButton = UIButton(type: .system)
        listenButton.setTitle("OUVIR", for: .normal)
        listenButton.backgroundColor = .white
        listenButton.setTitleColor(.blue, for: .normal)
        listenButton.translatesAutoresizingMaskIntoConstraints = false
        listenButton.tag = 5
        listenButton.addTarget(self, action: #selector(listenButtonTapped(_:)), for: .touchUpInside)
        productContentView.addSubview(listenButton)

        let viewButton = UIButton(type: .system)
        viewButton.setTitle("VER", for: .normal)
        viewButton.backgroundColor = .white
        viewButton.setTitleColor(.blue, for: .normal)
        viewButton.translatesAutoresizingMaskIntoConstraints = false
        viewButton.tag = 6
        viewButton.addTarget(self, action: #selector(viewButtonTapped(_:)), for: .touchUpInside)
        productContentView.addSubview(viewButton)

        NSLayoutConstraint.activate([
            activityIndicator.centerXAnchor.constraint(equalTo: loadingView.centerXAnchor),
            activityIndicator.centerYAnchor.constraint(equalTo: loadingView.centerYAnchor),

            loadingView.topAnchor.constraint(equalTo: productView.topAnchor),
            loadingView.leadingAnchor.constraint(equalTo: productView.leadingAnchor),
            loadingView.trailingAnchor.constraint(equalTo: productView.trailingAnchor),
            loadingView.bottomAnchor.constraint(equalTo: productView.bottomAnchor),

            imageView.topAnchor.constraint(equalTo: productContentView.topAnchor, constant: 10),
            imageView.leadingAnchor.constraint(equalTo: productContentView.leadingAnchor, constant: 10),
            imageView.widthAnchor.constraint(equalToConstant: 50),
            imageView.heightAnchor.constraint(equalToConstant: 60),

            titleLabel.topAnchor.constraint(equalTo: productContentView.topAnchor, constant: 10),
            titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10),
            titleLabel.trailingAnchor.constraint(equalTo: productContentView.trailingAnchor, constant: -10),

            listenButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
            listenButton.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 10),
            listenButton.widthAnchor.constraint(equalToConstant: 80),

            viewButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
            viewButton.leadingAnchor.constraint(equalTo: listenButton.trailingAnchor, constant: 10),
            viewButton.trailingAnchor.constraint(equalTo: productContentView.trailingAnchor, constant: -10),

            productContentView.topAnchor.constraint(equalTo: productView.topAnchor),
            productContentView.leadingAnchor.constraint(equalTo: productView.leadingAnchor),
            productContentView.trailingAnchor.constraint(equalTo: productView.trailingAnchor),
            productContentView.bottomAnchor.constraint(equalTo: productView.bottomAnchor)
        ])

        return productView
    }

    @objc private func listenButtonTapped(_ sender: UIButton) {
        if let productView = sender.superview?.superview as? UIView,
           let titleLabel = productView.viewWithTag(4) as? UILabel {
            print("OUVIR botão clicado para o produto: \(titleLabel.text ?? "")")
        }
    }

    @objc private func viewButtonTapped(_ sender: UIButton) {
        if let productView = sender.superview?.superview as? UIView,
           let titleLabel = productView.viewWithTag(4) as? UILabel {
            print("VER botão clicado para o produto: \(titleLabel.text ?? "")")
        }
    }

    @objc private func productViewTapped(_ gesture: UITapGestureRecognizer) {
        if let productView = gesture.view,
           let titleLabel = productView.viewWithTag(4) as? UILabel {
            print("Produto clicado: \(titleLabel.text ?? "")")
        }
    }
    
    private func updateProductView(productView: UIView, product: Product) {
        if let loadingView = productView.viewWithTag(1),
           let productContentView = productView.viewWithTag(2),
           let imageView = productContentView.viewWithTag(3) as? UIImageView,
           let titleLabel = productContentView.viewWithTag(4) as? UILabel {
            print("Atualizando a view do produto")
            loadingView.isHidden = true
            productContentView.isHidden = false
            imageView.image = product.image
            titleLabel.text = "\(product.title) (\(product.id))"
            productView.setNeedsLayout()
            productView.layoutIfNeeded()
        } else {
            print("Não foi possível encontrar as views para atualizar")
        }
    }

    private func imageFromView(view: UIView) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0)
        view.drawHierarchy(in: view.bounds, afterScreenUpdates: true)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image ?? UIImage()
    }

    private func loadProductData(productId: String, completion: @escaping (Product) -> Void) {
        // Simula carregamento assíncrono de dados
        DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
            let product = Product(id: productId, title: "Produto \(productId)", image: UIImage(systemName: "book")!)
            DispatchQueue.main.async {
                completion(product)
            }
        }
    }
}

struct Product {
    let id: String
    let title: String
    let image: UIImage
}

extension MainViewController: UITextViewDelegate {
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        return false
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant