显示来自URL的图片,Swift 4.2

时间:2018-11-11 18:42:50

标签: ios json swift nsdictionary

我是一个相当不错的Objective C开发人员,现在我正在学习Swift(我发现这非常困难,这不仅是因为新概念(例如可选),而且因为Swift不断发展,而且其中的许多可用的教程已经过时了。

目前,我正在尝试将url中的JSON解析为NSDictionary,然后使用其值之一显示图像(也是url)。像这样:


URL-> NSDictionary->从URL初始化UIImage->在UIImageView中显示UIImage


这在目标C中非常容易(甚至可能会有更短的答案):

NSURL *url = [NSURL URLWithString:@"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];

上面的代码片段为我提供了一个标准的NSDictionary,在其中我可以引用“ url”键来获取要显示的图像的地址:


“ url”:“ https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg


然后我将其转换为UIImage并将其提供给UIImageView:

NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:@"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];

UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];

现在,我基本上是想在Swift中复制上面的Objective C代码,但是会不断遇到麻烦。我尝试了一些教程(其中一个实际上做了完全相同的事情:显示NASA图像),还找到了一些堆栈溢出答案,但没有一个可以帮上忙,因为它们要么已经过时,要么它们的工作方式与我所需的有所不同。

因此,我想请社区为这些问题提供Swift 4代码:

1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image

如果还不算太多,我还想在代码旁边索取详细说明,因为我希望答案是该任务的一个综合“教程”,我相信它目前在任何地方都没有。 / p>

谢谢!

8 个答案:

答案 0 :(得分:4)

首先,我很确定半年后您会发现Objective-C非常复杂和困难。

不鼓励使用第二个ObjC代码。不要使用同步Data(contentsOf方法从远程URL加载数据。不管使用哪种语言,都使用(NS)URLSession之类的异步方式。

并且不要在Swift中使用Foundation集合类型NSArrayNSDictionary。如果有本机Swift副本,基本上根本不使用NS...类。

在Swift 4中,您可以轻松地使用Decodable协议将JSON直接解码为(Swift)结构,
URL字符串甚至可以解码为URL

创建结构

struct Item: Decodable {
    // let copyright, date, explanation: String
    // let hdurl: String
    // let mediaType, serviceVersion, title: String
    let url: URL
}

如果您需要的不仅仅是URL,请取消注释这些行。

并使用两个数据任务加载数据。

let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")! 

let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
    if let error = error { print(error); return }
    do {
       let decoder = JSONDecoder()
       // this line is only needed if all JSON keys are decoded
       decoder.keyDecodingStrategy = .convertFromSnakeCase
       let result = try decoder.decode(Item.self, from: data!)
       let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
           if let imageError = imageError { print(imageError); return }
           DispatchQueue.main.async {
               let apodImage = UIImage(data: imageData!)
               let apodView = UIImageView(image: apodImage)
               // do something with the image view
           }
       }
       imageTask.resume()
   } catch { print(error) }
}
task.resume()

答案 1 :(得分:1)

由于图像加载是一项琐碎的任务,同时可以用许多不同的方式实现,因此,我建议您不要“重新发明轮子”,而应该关注图像加载库,例如 { {3}} ,因为它已经涵盖了您在开发过程中可能需要的大多数情况。

它允许您使用简单的api将图像异步加载并显示到视图中:

Nuke.loadImage(with: url, into: imageView)

还有,如果需要-指定 图像的加载和显示方式:

let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
    success: .scaleAspectFill,
    failure: .center,
    placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)

答案 2 :(得分:1)

创建UIIimageView扩展和以下代码

extension UIImageView {
public func imageFromServerURL(urlString: String) {
    self.image = nil
    let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
    URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in

        if error != nil {
            print(error as Any)
            return
        }
        DispatchQueue.main.async(execute: { () -> Void in
            let image = UIImage(data: data!)
            self.image = image
        })

    }).resume()
}}

self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)

答案 3 :(得分:1)

我刚刚扩展了vadian的答案,分离了一些问题以清楚地了解基本知识。他的回答就足够了。

首先,您必须构建自己的结构。这将代表您从Web服务检索到的JSON结构。

struct Item: Codable {
    let url, hdurl : URL,
    let copyright, explanation, media_type, service_version, title : String
}

然后让您请求方法。我通常会为此创建一个单独的文件。现在,vadian提到了完成处理程序。这些由转义的闭包表示。在这里,闭包()->在两个函数上均被传递,并以解码后的数据作为参数来调用。

struct RequestCtrl {

    func fetchItem(completion: @escaping (Item?)->Void) {

         let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
         //URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
         let task = URLSession.shared.dataTask(with: url) { (data, _, _) in 
             let jsonDecoder = JSONDecoder()
             if let data = data,
                let item = try? jsonDecoder.decode(Item.self, from: data){
                //jsonDecoder requires a type of our structure represented by .self and the data from the request.  
                completion(item)
             } else {
                 completion(nil)
             }
          }
         task.resume()
    }


    func fetchItemPhoto(usingURL url: URL, completion: @escaping (Data?)-> Void) {
         let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
            if let data = data { completion(data) } else { completion(nil) }
          }
         task.resume()
    }
}

现在,在ViewController中,调用您的请求并处理关闭的执行。

  class ViewController: UIViewController {

      let requestCtrl = RequestCtrl()

      override func viewDidLoad() {
         super.viewDidLoad()

         requestCtrl.fetchItem { (fetchedItem) in
            guard let fetchedItem = fetchedItem else { return }
            self.getPhoto(with: fetchedItem)
         }

      }

      func getPhoto(with item: Item) {
           requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
                 guard let fetchedPhoto = fetchedPhoto else { return }
                 let photo = UIImage(data: fetchedPhoto)
                  //now you have a photo at your disposal 
           }
      }
  }

这些并不是最佳实践,因为我还在学习,所以一定要对一些主题进行一些研究,尤其是Apple文档中的闭包,ios并发和URLComponents :)

答案 4 :(得分:1)

您可以使用此扩展名

extension UIImage {

    public static func loadFrom(url: URL, completion: @escaping (_ image: UIImage?) -> ()) {
        DispatchQueue.global().async {
            if let data = try? Data(contentsOf: url) {
                DispatchQueue.main.async {
                    completion(UIImage(data: data))
                }
            } else {
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        }
    }

}

使用

guard let url = URL(string: "http://myImage.com/image.png") else { return }

UIImage.loadFrom(url: url) { image in
    self.photo.image = image
}

答案 5 :(得分:0)

您需要将url转换为字符串和数据以添加到imageview

let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)

答案 6 :(得分:0)

首先在Podfile中添加Pod 豆荚“ Alamofire”, 豆荚“ AlamofireImage” 您可以检查此链接以获取安装Pod => https://cocoapods.org/pods/AlamofireImage

//使用此功能从imageview中的URL加载图像

    public class MainActivity extends AppCompatActivity {
        List<String> tab_name = new ArrayList<>();
        Map<String, ArrayList<Model>> map = new HashMap<>();
        TabLayout tabLayout;
        ViewPager viewPager;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            tabLayout = findViewById(R.id.tabs);
            viewPager = findViewById(R.id.container);

            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://channelkonnect.com/ifb/userApi/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
            final RequestInterface request = retrofit.create(RequestInterface.class);
            Call<JsonObject> call = request.getJSON();
            call.enqueue(new Callback<JsonObject>() {
                @Override
                public void onResponse(@NonNull Call<JsonObject> call, @NonNull Response<JsonObject> response) {
                    JsonObject json_response = response.body();
                    assert json_response != null;
                    Gson gson = new Gson();

                    JSONObject object = null;
                    try {
                        object = new JSONObject(gson.toJson(json_response));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    JSONObject forecaseobject = null;
                    try {
                        assert object != null;
                        forecaseobject = (JSONObject) object.get("forecast");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                    JSONObject month;
                    try {
                        assert forecaseobject != null;
                        month = (JSONObject) forecaseobject.get("month");
                        Iterator iterator = month.keys();
                        while (iterator.hasNext()) {
                            // modelList.clear();
                            String key = iterator.next().toString();
                            Log.d("Key", key);
                            tab_name.add(key);
                            JSONArray jsonArray = month.getJSONArray(key);
                            ArrayList<Model> modelList = new ArrayList<>();
                            for (int j = 0; j < jsonArray.length(); j++) {
                                JSONObject tempObj = jsonArray.getJSONObject(j);
                                String id = tempObj.getString("prod_name");
                                String price = tempObj.getString("price");
                                String product = tempObj.getString("prodId");
                                String qty = tempObj.getString("Qty");
                                String date = tempObj.getString("date");
                                modelList.add(new Model(id, price, product, qty, date));
                                map.put(key, modelList);
                            }
                        }
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    Log.d("aspect detail", "outside while:  " + map);
                    for (int i = 0; i < map.size(); i++) {
                        tabLayout.addTab(tabLayout.newTab().setText(tab_name.get(i)));
                    }
                    ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager(), tabLayout.getTabCount());
                    viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
                    viewPager.setAdapter(adapter);
                    tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));


                }//on_response

                @Override
                public void onFailure(@NonNull Call<JsonObject> call, @NonNull Throwable t) {
                }//on_failure
            });//enqueue
        }//on create

        class ViewPagerAdapter extends FragmentPagerAdapter {
            private int mNumOfTabs;


            ViewPagerAdapter(FragmentManager fm, int NumOfTabs) {
                super(fm);
                this.mNumOfTabs = NumOfTabs;
            }

            @Override
            public Fragment getItem(int position) {
// guys here i'm sending the arrayList of a particular month
                ArrayList<Model> tempmodel = map.get(tab_name.get(position));
                return Detailfragment.newInstance(tempmodel);
            }

            @Override
            public int getCount() {
                return mNumOfTabs;
            }
    //                @Override
    //                public CharSequence getPageTitle(int position) {return super.getPageTitle(position); }
        }


        private interface RequestInterface {
            @GET("getForecast?userCode=EMP001&chnlid=2")
            Call<JsonObject> getJSON();
        }
    }

检查此链接以获取alamofireImage的方法 https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md

答案 7 :(得分:0)

用于异步图像下载器的用户SDWebImage第三方库

import SDWebImage

imageView.sd_setImage(with: URL(string: url), placeholderImage: UIImage(named: "placeholder.png"))