Реализация In-App Feedback (скриншот + аннотация + описание) в мобильном приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.
Разработка и поддержка любых видов мобильных приложений:
Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Предлагаемые услуги
Показано 1 из 1 услугВсе 1735 услуг
Реализация In-App Feedback (скриншот + аннотация + описание) в мобильном приложении
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1054
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    445

Реализация In-App Feedback (скриншот + аннотация + описание) в мобильном приложении

Классические feedback-формы дают слабый контекст: пользователь пишет «кнопка не работает», разработчик не понимает, какая кнопка и при каких условиях. In-App Feedback с захватом скриншота и инструментом аннотации решает эту проблему — пользователь буквально показывает, что не так. Уровень детализации репортов вырастает многократно.

Захват скриншота

iOS — UIGraphicsImageRenderer

func captureScreenshot() -> UIImage? {
    let renderer = UIGraphicsImageRenderer(bounds: UIScreen.main.bounds)
    return renderer.image { ctx in
        UIApplication.shared.windows.first?.layer.render(in: ctx.cgContext)
    }
}

Важный нюанс: layer.render не захватывает контент WKWebView и ARSCNView — они рендерятся через отдельный GPU-контекст. Для WebView нужен WKWebView.takeSnapshot(with:):

webView.takeSnapshot(with: nil) { image, error in
    // Вставить в итоговый скриншот через Core Graphics
}

Android — PixelCopy API

До Android 8.0 использовали View.getDrawingCache(), но он не захватывает SurfaceView и TextureView (видео, карты, камера). С Android 8.0+ рекомендуется PixelCopy:

fun captureScreenshot(activity: Activity, callback: (Bitmap?) -> Unit) {
    val bitmap = Bitmap.createBitmap(
        activity.window.decorView.width,
        activity.window.decorView.height,
        Bitmap.Config.ARGB_8888
    )
    PixelCopy.request(activity.window, bitmap, { result ->
        callback(if (result == PixelCopy.SUCCESS) bitmap else null)
    }, Handler(Looper.getMainLooper()))
}

Для Flutter используется RenderRepaintBoundary:

Future<ui.Image> captureWidget(GlobalKey key) async {
    final boundary = key.currentContext!.findRenderObject()
        as RenderRepaintBoundary;
    return boundary.toImage(pixelRatio: 3.0);
}

Инструмент аннотации

После захвата скриншота пользователь должен выделить проблемную область. Базовый набор инструментов: маркер (рисование от руки), стрелка, прямоугольное выделение, текстовая метка. Опционально — пикселизация (blur) для скрытия приватных данных перед отправкой.

Реализация canvas на iOS

class AnnotationCanvasView: UIView {
    private var paths: [UIBezierPath] = []
    private var currentPath: UIBezierPath?
    var strokeColor: UIColor = .red
    var strokeWidth: CGFloat = 3.0

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let path = UIBezierPath()
        path.move(to: touches.first!.location(in: self))
        currentPath = path
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        currentPath?.addLine(to: touches.first!.location(in: self))
        setNeedsDisplay()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        if let path = currentPath { paths.append(path) }
        currentPath = nil
    }

    override func draw(_ rect: CGRect) {
        for path in paths {
            strokeColor.setStroke()
            path.lineWidth = strokeWidth
            path.stroke()
        }
        strokeColor.setStroke()
        currentPath?.stroke()
    }
}

Итоговый аннотированный скриншот — merge canvas layer поверх screenshot image через UIGraphicsImageRenderer.

Готовые библиотеки

Если нет задачи писать canvas с нуля: PSPDFKit Annotations (платный, профессиональный), Pen (iOS, open source), Annotatable (Flutter). Для большинства продуктовых задач кастомный canvas занимает 2–3 дня разработки и даёт полный контроль над UX.

Сбор метаданных

К скриншоту автоматически прикрепляем:

struct FeedbackPayload: Encodable {
    let screenshot: Data          // JPEG, качество 0.7
    let description: String
    let appVersion: String
    let osVersion: String
    let deviceModel: String
    let screenName: String        // текущий экран (роутер/NavigationStack)
    let userId: String?
    let sessionId: String         // UUID сессии для корреляции с логами
    let timestamp: Date
}

screenName особенно важен — позволяет сразу понять, на каком экране возникла проблема, без расспросов пользователя.

Отправка и хранение

Скриншот отправляем как multipart/form-data. Для хранения на backend — S3 или аналог с pre-signed URL. В тикет-систему (Jira, Linear, Sentry) прикрепляем ссылку на изображение.

Пример через Sentry:

let attachment = Attachment(
    data: screenshotData,
    filename: "screenshot.jpg",
    contentType: "image/jpeg"
)
SentrySDK.capture(message: feedback.description) { scope in
    scope.addAttachment(attachment)
    scope.setTag(value: feedback.screenName, key: "screen")
}

Ориентиры по срокам

Кастомная реализация с canvas-аннотацией, захватом скриншота и отправкой в Jira/Sentry — 1–1.5 недели. Интеграция готовой библиотеки аннотаций с кастомным UI обёртки — 3–5 дней.