我是一个相当不错的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>
谢谢!
答案 0 :(得分:4)
首先,我很确定半年后您会发现Objective-C非常复杂和困难。
不鼓励使用第二个ObjC代码。不要使用同步Data(contentsOf
方法从远程URL加载数据。不管使用哪种语言,都使用(NS)URLSession
之类的异步方式。
并且不要在Swift中使用Foundation集合类型NSArray
和NSDictionary
。如果有本机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"))