Загрузка значка GMSMarker по URL-адресу

Для установки значка на GMSMarker в Google Maps SDK требуется UIImage, но в настоящее время мои требования заключаются в загрузке его из определенного URL

Проблема

Проблема в том, что почему-то иногда показывается только последний элемент. Это код того, как я создаю маркеры (Обновлено в Swift)

func createMarkers() {
    mapView.clear()

    let mapCoordinates: [CLLocationCoordinate2D] = coordinates()
    let iconURLs: [URL] = imageURLs()

    var marker = GMSMarker()
    for i in 0..<mapCoordinates.count {
        let imageURL = iconURLs[i]

        marker = GMSMarker()
        marker.position = mapCoordinates[i]
        marker.map = mapView

        downloadImage(from: imageURL) { image in
            marker.icon = image
        }
    }
}

// It is a helper function calling `SDWebImage` which caches the `UIImage` based on its `URL` string
func downloadImage(from url: URL, completion: @escaping (UIImage) -> Void)

Из приведенного выше кода у меня возникают проблемы при первой загрузке данных, потому что булавки отображаются на карте, но без изображения. Если через какое-то время снова вызвать createMarkers(), значки загружаются корректно.

Я не знаю, почему это происходит, какие-либо предложения или подсказки, чтобы решить эту проблему?


person E-Riddie    schedule 12.06.2014    source источник
comment
Привет, у тебя есть какое-нибудь решение?? я хотел реализовать ленивую загрузку изображений маркеров, но изображения не отображаются. я пытаюсь сделать с классом AsyncImageview. Пожалуйста, скажите мне   -  person Nisha    schedule 05.09.2014
comment
@Nisha Вы не можете выполнять ленивую загрузку с ним. Вам нужно загрузить все изображения асинхронно, а когда загрузка будет завершена, добавить изображения для пинов.   -  person E-Riddie    schedule 05.09.2014
comment
окей спасибо за ответ. Теперь это решается с помощью Grand Central Dispatch.   -  person Nisha    schedule 05.09.2014
comment
@Nisha Да, вы можете загружать их асинхронно, но как вы отслеживаете, какое изображение загружается?   -  person E-Riddie    schedule 05.09.2014
comment
прямо сейчас мне не нужно отслеживать .. я использовал dispatch_async (myQueue, ^ {}); метод в цикле for может быть я могу получить с помощью objectAtIndex: я не уверен   -  person Nisha    schedule 05.09.2014
comment
@Nisha Если у вас есть одна булавка, это очень просто. Но когда у вас много пинов, для которых требуются изображения, это главное. Вам нужно знать, закончились ли все загрузки, и загрузить их все. Как вам это удается? Попробуйте смоделировать это при плохом соединении на iOS Simulator и сделайте выводы!   -  person E-Riddie    schedule 05.09.2014
comment
мне не нужно ждать всех загрузок.. маркеры загружаются один за другим.. посмотрите это видео   -  person Nisha    schedule 05.09.2014
comment
@Nisha Ага, теперь я понимаю твою точку зрения! Хороший способ добавить его, но когда у вас плохое соединение, загрузка изображения будет медленной, и контакты будут ждать их показа. Я показываю все контакты (изображение по умолчанию), и после загрузки всех изображений я загружаю их все. В любом случае это зависит от требований вашего приложения   -  person E-Riddie    schedule 05.09.2014
comment
хорошо, что хорошие булавки не придется ждать, используя вашу технику.   -  person Nisha    schedule 05.09.2014


Ответы (4)


Просто используйте SDWebimage и .iconView, а не .icon

let imageView = UIImageView(image: pinImage)
imageView.sd_setImage(with: URL(string: "https://pbs.twimg.com/profile_images/469017630796296193/R-bEN4UP.png"), placeholderImage: pinImage)
marker.iconView = imageView
person hoangtuanfithou    schedule 24.04.2017

Хорошо, тогда я мало что знал о параллелизме, и просмотр всех ответов на самом деле не решал того, что я действительно имел в виду.

Проблема

Поскольку загрузка изображения из URL (downloadImage(from url: URL, completion: @escaping (UIImage) -> Void)) является фоновой операцией, к тому времени, когда изображение будет загружено, marker выйдет за пределы области действия, будучи снова инициализированным в цикле.

Таким образом, невозможно узнать, какое изображение было привязано к какому маркеру.

Кроме того, относительно Почему это работает при перезагрузке?, SDWebImage при перезагрузке уже загрузил и кэшировал изображение, поэтому в следующий раз задержки нет, оно просто привязано к marker напрямую.

Решение

Переместите маркеры изменяемой переменной как неизменяемое значение внутри цикла.

func createMarkers() {
    let mapCoordinates: [CLLocationCoordinate2D] = coordinates()
    let iconURLs: [URL] = imageURLs()

    for i in 0..<mapCoordinates.count {
        let imageURL = iconURLs[i]

        let marker = GMSMarker()
        marker.position = mapCoordinates[i]
        marker.map = mapView
        applyImage(from: imageURL, to: marker)
    }
}

Теперь вы видите, что я представил вспомогательную функцию под названием applyImage(from:, to:), которая в основном ничего особенного не делает, а загружает изображение из URL и привязывает его к аргументу типа GMSMarker.

func applyImage(from url: URL, to marker: GMSMarker) {
    DispatchQueue.global(qos: .background).async {
        guard let data = try? Data(contentsOf: url),
            let image = UIImage(data: data)?.cropped()
            else { return }

        DispatchQueue.main.async {
            marker.icon = image
        }
    }
}

Дополнительно есть расширение для обрезки изображения до 44pt X 44pt, это неправильно, но это просто для предварительного просмотра того, что было загружено.

extension UIImage {

    func cropped() -> UIImage? {
        let cropRect = CGRect(x: 0, y: 0, width: 44 * scale, height: 44 * scale)

        guard let croppedCGImage = cgImage?.cropping(to: cropRect) else { return nil }

        return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation)
    }
}

ПРИМЕЧАНИЕ. Весь код ViewController можно найти в Gist, который я написал!

Выход

Демо

person E-Riddie    schedule 28.08.2018
comment
отличное решение дружище - person ProjektWeinheim; 18.03.2020

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

[imageView setImageWithURL:[NSURL URLWithString:placeObject.photoPath] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{
    image = [self image:image ByScalingAndCroppingForSize:CGSizeMake(44, 44)];
    [marker setIcon:image];
    [pins addObject:marker]; //assuming you use pins later
    marker.map = mapView_;
}];

так как у вас уже есть маркер, над которым вы работаете. конечно, вам нужно будет обратить внимание на ваш обработчик ошибок.

person gro    schedule 12.06.2014
comment
Я пробовал это раньше, тоже не повезло! У меня нет ошибок, просто маркер игнорирует изображение. - person E-Riddie; 12.06.2014
comment
затем проверьте ваш cacheType. - person gro; 12.06.2014
comment
У меня нет проблем с кешем и ошибок, мне просто нужно, как правильно загрузить изображение в маркер - person E-Riddie; 12.06.2014
comment
Вы сказали это ранее: «Если я перезагружаю данные из Интернета и снова использую этот метод для добавления булавок, изображение загружается на маркеры». Разве это не так? - person gro; 12.06.2014
comment
Да, но я хочу загружать изображения на маркеры без перезагрузки. Есть вероятность, что изображения загружаются, и когда я нажимаю обновить, изображения уже находятся в кеше! - person E-Riddie; 12.06.2014
comment
попробуйте блок dispatch_sync - person gro; 12.06.2014
comment
Я могу использовать его, но это то же самое, что использовать SDWebImage. - person E-Riddie; 12.06.2014
comment
Я считаю, что SDWebImage является асинхронной/кэш-библиотекой. Вы хотите дождаться изображения, верно? - person gro; 12.06.2014
comment
не может быть и того, и другого... ну, по крайней мере, в этом сценарии. Если вы знаете изображения, которые вам нужны, достаточно заранее, сделайте вызов, чтобы получить их в кэше, прежде чем вы перейдете к методу добавления маркеров. - person gro; 12.06.2014
comment
Как сделать решение, которое говорит мне, что все изображения загружены. Это может решить мою проблему! - person E-Riddie; 12.06.2014
comment
Может быть, счетчик в обработчике успеха, чтобы соответствовать количеству выводов? или условие, выполненное в обработчике успеха запроса на окончательное изображение булавки (например, последний URL-адрес соответствует) - person gro; 12.06.2014
comment
Да, но если последнее изображение имеет наименьший размер и загружается быстрее остальных? Вот что вызывает у меня сомнения. Кроме того, я не могу назначить счетчик для завершенияHandler, которого нет в его потоке. - person E-Riddie; 12.06.2014
comment
чувак, похоже, у них есть некоторые методы, которые ты должен использовать... обратные вызовы прогресса/завершения, запуск/остановка загрузки и т. д. github.com/rs/SDWebImage/blob/master/SDWebImage/ - person gro; 12.06.2014
comment
Вы не получаете его, обратный вызов происходит, когда изображение завершено, поэтому я знаю, когда одно изображение загружается, но моя проблема заключается в том, как их подсчитать. Я нахожусь в фоновом потоке и не могу назначить его счетчик, вот в чем дело - person E-Riddie; 12.06.2014

такая же проблема. И это мое решение: добавить 2 раза

Это мой код :D

- (void)addMarkers {
    //for() {
         NSString *avatarLink = [tempLocation.userInfo objectForKey:@"avatar"];
        GMSMarker *marker = [self addMarker:tempLocation andCustomView:customView];

        [customView.imageGame setImageWithURL:[NSURL URLWithString:avatarLink] placeholderImage:[UIImage imageNamed:LOADING_ICON] options:SDWebImageCacheMemoryOnly completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
            marker.icon = [self captureView:customView];
        }];
    //}
}
- (GMSMarker*)addMarker: (FriendLocation*)tempLocation andCustomView: (FriendCustomInforwindow*)customView {
    GMSMarker *marker = [[GMSMarker alloc] init];
    marker.position = CLLocationCoordinate2DMake(tempLocation.coordinate.latitude, tempLocation.coordinate.longitude);
    marker.animated=YES;
    marker.infoWindowAnchor = CGPointMake(0.5, 2.37);
    marker.icon = [self captureView:customView];
    marker.map = mapView;
    [markersArray addObject:marker];
    return marker;
}
person user2181683    schedule 12.06.2014
comment
Что делает «[self captureView:customView]»? - person E-Riddie; 12.06.2014
comment
О, я настраиваю InfoWindow. Я должен преобразовать uiview в uiimage. В customView я добавляю uiimageview: он называется imageGame. - person user2181683; 12.06.2014
comment
Нет никакой разницы с моей функцией, это просто та же идея, только другой код! - person E-Riddie; 12.06.2014
comment
верно, надо подождать. Или вам нужно загрузить и кэшировать его, прежде чем добавлять маркеры. - person user2181683; 12.06.2014
comment
В любом случае, спасибо за ваш код, но моя проблема заключается в том, как правильно их загрузить, потому что у меня слишком много булавок на карте. - person E-Riddie; 12.06.2014