2016-11-04 2 views
1

Я собираюсь закончить свое первое приложение для iOS, но у меня возникла проблема, которую я не знаю, как ее решить.Изменено представление свойств контроллера Перед просмотром контроллера отображается

Исходный ViewController приложения содержит MKMapView, у которого мало аннотаций. При нажатии любой аннотации появляется всплывающее окно с некоторой информацией, а затем появляется информационная кнопка, которая ведет к новому ViewController, с подробной информацией, относящейся к выбранной аннотации.

Дело в том, что второй ViewController (DetailViewController) имеет несколько ярлыков (название, описание), изображение и несколько ссылок, которые должны быть загружены до отображения самого ViewController. Я читаю эти значения из JSON, но с этим нет никаких проблем.

Я читаю все значения и устанавливать их в ViewDidLoad()

Так что вещь, когда эта вторая ViewController (DetailedViewController) загружаются в тренажере или в физическом iPhone, все поля имеют свои по умолчанию (тот, который установлен в раскадровке), и тратит несколько секунд на обновление с нужными значениями, даже они быстро считываются из JSON (согласно журналам).

Кроме того, ошибка появляется в консоли:

This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes. 
Stack:(
    0 CoreFoundation      0x000000018a0ca1d8 <redacted> + 148 
    1 libobjc.A.dylib      0x0000000188b0455c objc_exception_throw + 56 
    2 CoreFoundation      0x000000018a0ca108 <redacted> + 0 
    3 Foundation       0x000000018acb1ea4 <redacted> + 192 
    4 Foundation       0x000000018acb1be4 <redacted> + 76 
    5 Foundation       0x000000018aafd54c <redacted> + 112 
    6 Foundation       0x000000018acb0880 <redacted> + 112 
    7 UIKit        0x000000018ff2140c <redacted> + 1688 
    8 QuartzCore       0x000000018d3e1188 <redacted> + 148 
    9 UIKit        0x000000019056de90 <redacted> + 64 
    10 QuartzCore       0x000000018d3d5e64 <redacted> + 292 
    11 QuartzCore       0x000000018d3d5d24 <redacted> + 32 
    12 QuartzCore       0x000000018d3527ec <redacted> + 252 
    13 QuartzCore       0x000000018d379c58 <redacted> + 512 
    14 QuartzCore       0x000000018d37a124 <redacted> + 660 
    15 libsystem_pthread.dylib    0x000000018915efbc <redacted> + 572 
    16 libsystem_pthread.dylib    0x000000018915ece4 <redacted> + 200 
    17 libsystem_pthread.dylib    0x000000018915e378 pthread_mutex_lock + 0 
    18 libsystem_pthread.dylib    0x000000018915dda4 start_wqthread + 4 
) 

Я читал подобные вопросы, и они говорят о чем-то вроде этого:

dispatch_async(dispatch_get_main_queue(){ 
    // code here 
}) 

Но я не знаю точно как использовать его :(

кто-нибудь может мне помочь

большое спасибо заранее:)

EDIT:

Я добавлю код моего подробного контроллера представления, более точно, что происходит объяснить:

class BarDetailsViewController: UIViewController { 

@IBOutlet var BarDetailsView: UIView! 
@IBOutlet weak var BarNameLabel: UILabel! 
@IBOutlet weak var BarDescriptionLabel: UILabel! 

@IBOutlet weak var BarImage: UIImageView! 

var barPhoneNumber: AnyObject! = "anyBarPhoneNumber" 
var barWebsite: AnyObject! = "anyBarWebsite" 
var barName: AnyObject! = "anyBarName" 

var pinCoordinates : CLLocationCoordinate2D! 
var pinId : String! 

override func viewDidLoad() { 
    super.viewDidLoad() 

    let jsonURL: String = "http://www.craftbr.me/services/getbiz/index.php?barid=" + self.pinId 
    let requestURL: NSURL = NSURL(string: jsonURL)! 

    let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL) 
    let session = NSURLSession.sharedSession() 
    let task = session.dataTaskWithRequest(urlRequest) { 
     (data, response, error) -> Void in 

     let httpResponse = response as! NSHTTPURLResponse 
     let statusCode = httpResponse.statusCode 

     if (statusCode == 200) { 
      do{ 
       let json: AnyObject! = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) 

       let barName: AnyObject! = json["name"] 
       self.BarNameLabel.text = String(barName) 
       self.barName = String(barName) 

       self.BarDescriptionLabel.text = (json["description"] as! String) 

       self.barPhoneNumber = json["telf"] 

       self.barWebsite = json["website"] as! String 

       let barImgUrl: AnyObject! = json["imgurl"] 
       let barImgUrlString: String! = String(barImgUrl) 

       if let imageUrl = NSURL(string: barImgUrlString), 
        let data = NSData(contentsOfURL: imageUrl) 
       { 
        self.BarImage.image = UIImage(data: data) 
       } 

      }catch { 
       print("Error with Json: \(error)") 
      } 
     } 
    } 
    task.resume() 
} 

} 

Так вот, внутри ViewDidLoad() я изменить метки контента, созданного в Раскадке с информацией, полученной от json.

Дело в том, что информация из json так быстра, но тогда, изменение меток - это медленная работа и выдает ошибку.

Так что @fragilecat, где я должен добавить функцию? И как объявить DispatchQueue? Я пробовал, но он не работает. И эта функция, где нужно называть?

Еще раз спасибо за вашу помощь :)

+0

Отъезд [Параллельное программирование Руководство] (https://developer.apple.com/library/content/documentation/General/Conceptual /ConcurrencyProgrammingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008091). – Adam

ответ

0

Apple использует очереди для представления потоков, так как @Adam говорит в комментариях, что вы должны посмотреть на Concurrency Programming Guide, чтобы лучше понять поведение вашего приложения.

Однако, возможно, было бы полезно объяснить, что происходит с вашим приложением, что оно создает ваш сбой, поскольку это очень распространенный шаблон в iOS.

Вы делаете запрос на некоторые веб-службы. Если это явно не произойдет, это будет происходить в фоновом режиме. Если бы это было не так, пользовательский интерфейс вашего приложения замерзал, пока запрос на веб-службу был сделан. Здесь нужно помнить, что ВСЕ-работа должна выполняться в главной очереди! @Leon Guo показал вам, как получить доступ к основной очереди из другой очереди.

Как это влияет на ваш запрос веб-службы. Ну, когда ответ приходит обратно и вы разобрать его, и вы готовы присвоить данные элементы пользовательского интерфейса, такие как этикетки и текстовые поля, вы хотите сделать это на главной очереди так:

// comment: example method on DetailedViewController class 
func prepareUI(data: MyDataType){ 
    // Swift 3 
    DispatchQueue.main.async { 
     titleLabel.text = data.titleString 
     descriptionLabel.text = data.descriptionText 
    } 
} 

Или другой мудрый вам будет обращаться к элементам ui в DetailViewController из очереди, отличной от основной очереди, следовательно, ваш сбой. Поэтому, чтобы решить вашу проблему прямо сейчас, привяжите назначение элементов пользовательского интерфейса в блоке кода DispatchQueue.main.async, где бы вы ни занимались. Это должно помочь вам снова и снова работать.

В долгосрочной перспективе вам необходимо разработать понимание того, как параллелизм реализован в iOS. The App Programming Guide for iOS - отличное место для начала!

Редактировать

Вот ваш код с помощью функции dispatch_async(dispatch_get_main_queue()). Я бы утверждать, что это просто, чтобы вы и работает ...

import UIKit 

class BarDetailsViewController: UIViewController { 

    @IBOutlet var barDetailsView: UIView! 
    @IBOutlet weak var barNameLabel: UILabel! 
    @IBOutlet weak var barDescriptionLabel: UILabel! 

    @IBOutlet weak var BarImage: UIImageView! 

    var barPhoneNumber = "anyBarPhoneNumber" 
    var barWebsite = "anyBarWebsite" 
    var barName = "anyBarName" 

    var pinCoordinates : CLLocationCoordinate2D! 
    var pinId : String! 

    override func viewDidLoad() { 
     super.viewDidLoad() 

    } 

    override func viewWillAppear(_ animated: Bool) { 
     super.viewWillAppear(animated) 

     // For example purposes only 
     networkCall() 

    } 



    func networkCall() { 
     let jsonURL: String = "http://www.craftbr.me/services/getbiz/index.php?barid=" + self.pinId 
     let requestURL: NSURL = NSURL(string: jsonURL)! 

     let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: requestURL) 
     let session = NSURLSession.sharedSession() 
     let task = session.dataTaskWithRequest(urlRequest) { 
      (data, response, error) -> Void in 

      let httpResponse = response as! NSHTTPURLResponse 
      let statusCode = httpResponse.statusCode 

      if (statusCode == 200) { 
       do{ 

        let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) 

        // Here we get back on the main queue since we are altering UI elements 
        dispatch_async(dispatch_get_main_queue()) { 
         let barName: AnyObject! = json["name"] 
         self.barNameLabel.text = String(barName) 
         self.barName = String(barName) 

         if let text = json["description"] as? String { 
          self.barDescriptionLabel.text = text 
         } 

         if phoneNumber = json["telf"] as? String { 
          self.barPhoneNumber = phoneNumber 
         } 

         if webSite = json["website"] as? String { 
          self.barWebsite = barWebsite 
         } 

         let barImgUrl: AnyObject = json["imgurl"] 
         let barImgUrlString: String = String(barImgUrl) 

         if let imageUrl = NSURL(string: barImgUrlString), let data = NSData(contentsOfURL: imageUrl) { 
          self.BarImage.image = UIImage(data: data) 
         } 
        } 


       }catch { 
        print("Error with Json: \(error)") 
       } 
      } 
     } 
     task.resume() 
    } 

} 
+0

Спасибо за ваш ответ. Я редактировал сообщение, добавляя дополнительную информацию. Где я должен назвать эту функцию? И как объявить DispatchQueue? Я пробовал это, но я не могу заставить его работать :( –

+0

Я посмотрю на него. –

+0

Теперь посмотрите. –

0

Все обновления операций о пользовательском интерфейсе должно быть сделано в основном потоке, например, изменение фона зрения в контроллере представления, вы можете сделать так:

DispatchQueue.main.async { 
     self.view.backgroundColor = UIColor.red 
    } 
+0

У GCD API были некоторые изменения в Swift 3.0 –

+0

Прежде всего, спасибо за ваш быстрый ответ :) Простите меня за мое невежество, но я не настолько опытен в разработке iOS, и я немного смущен. Когда вы говорите об основном потоке, вы имеете в виду, что я должен поместить этот код в DetailViewController, но вне функции ViewDidLoad? И как объявить DispatchQueue? Еще раз спасибо за помощь :) –