i've got this type of problem :
With my app i'm able to connect my iPhone with a BLE device so when i connect their together i'm transported into an another view.
Into this view i've to check always if i'm connected yet in a thread func.
If my connection is lost i've to call the performSegueWithIdentifier method.
But... When i do it, xCode give me back a bad report that says :
This application is modifying the autolayout engine from a background
thread, which can lead to engine corruption and weird crashes. This
will cause an exception in a future release.
This is my short code :
override func viewDidLoad(){
super.ViewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
while(true){
if(serial.connectedPeripheral == nil){
self.GoBackToBLE()
}
}
}
}
I want to precise that i can't use the method dispatch_get_main_queue because if i use that i can't do anything else into the view for example if i have a button I want to be able to press it and if i use dispatch_get_main_queue method i can't.
any suggestions? thanks a lot
You shouldn't do anything related to UI in dispatch_get_global_queue
you can split the things to run into 2 parts, 1 is related to UI other one is doing network calls/calculations etc.
override func viewDidLoad(){
super.ViewDidLoad()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// do the ui stuff here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
// do everything which is not ui
})
})
}
Related
I know that this question was asked a couple of times, but other solutions didn't work for me.
I have this model:
var notifications = [Notification]()
Notification:
let user: User (name, profileimage, etc)
let items: [Movies] (movie has image, name, etc)
So i display my notifications inside a tableview, each cell has profile info at the top and collectionview with items bellow.
Inside my tableviewcell, i have to reloadData of my collectionview to display correct movies. I know that probably reloadData method causes this lagging, but are there any solutions to avoid it?
TableViewcell:
var notification: Notification! {
didSet {
collectionView.reloadData();
}
}
I also tried this thing inside tablecell and call this method in willDisplayTableViewcell, but it doesn't help at all:
func setCollectionViewDataSourceDelegate(forRow row: Int) {
collectionView.delegate = self
collectionView.dataSource = self
collectionView.reloadData()
}
Images are loading using kingfisher, so it's fine in other places in my project.
userProfileImage.kf.setImage(
with: URL(string: profileImage),
options: [
.processor(DownsamplingImageProcessor(size: CGSize(width: 175, height: 175))),
.scaleFactor(UIScreen.main.scale),
.transition(.fade(0.2)),
]
)
I would comment out the code you have shown above as it will only get in the way and give false readings. My gut feeling is somehow you have managed to directly wire your notifications to your tableview / collection view and they are running on a background thread. What I would do to test this theory is wrap code anywhere where the UI is going to be updated with a perform on main thread like this
DispatchQueue.main.async { [unowned self] in
//Your UI update code within cellforRow / cellForItem (both table and collection view) Here
}
Try doing this one by one until you find the culprit and for a better solution try to refactor so you don't have to force this Main thread directive.
I'm currently trying to implement Reachability into my current project. I followed a tutorial on YouTube that worked but I'm unsure whether or not its the correct way of doing it. In the Reachability documentation (https://github.com/ashleymills/Reachability.swift) it shows two examples first one being 'Example - closures' where I assume it's done in the viewDidLoad?
//declare this property where it won't go out of scope relative to your listener
let reachability = Reachability()!
reachability.whenReachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
if reachability.isReachableViaWiFi() {
print("Reachable via WiFi")
} else {
print("Reachable via Cellular")
}
}
}
reachability.whenUnreachable = { reachability in
// this is called on a background thread, but UI updates must
// be on the main thread, like this:
DispatchQueue.main.async {
print("Not reachable")
}
}
do {
try reachability.startNotifier()
} catch {
print("Unable to start notifier")
}
and the last example was 'Example - notifications', this is where I get confused the creator says to do that all in viewDidAppear. Is there really a big difference if I just do everything inside viewDidLoad? Does it change the outcome of anything? It currently works fine but I'm not sure whether it's right, I don't want it affecting me in the future. Any help would be great! Thanks.
It depends on your needs.
If you want to use Reachability...
... dynamically only if this particular view is frontmost, startNotifier() in viewWillAppear and stopNotifier() in viewDidDisappear.
... in this particular view as long as the view is alive/loaded startNotifier() in viewDidLoad.
... globally in all views put the entire code in AppDelegate and post notifications.
Problem solved. See end of post.
Sorry if this is a bit long but I'm hoping I've included as much info to get this solved.
Brief overview of problem: Enter value in a textField using my custom keypad. Tap done button(should trigger view.endEditing) and some textFields will cause the app to freeze, most the time Xcode won't even throw an error but instead just restart the app, but i did catch one once(pic below). It works as expected on some textFields.
So I have a view controller with a bunch of textFields for the user to fill out which then performs calculations.
I have made a custom Keypad which essentially is the decimal pad with a "Done" button. I did this by making an keyboard.xib file and a keyboard.swift file.
Heres a snapshot of the error, I've included a whole bunch of my code below incase I'm using a method that isn't the best.
This is how the keyboard.swift file looks:
import UIKit
// The view controller will adopt this protocol (delegate)
// and thus must contain the keyWasTapped method
protocol KeyboardDelegate: class {
func keyWasTapped(character: String)
func keyDone()
func backspace()
}
class keyboard: UIView {
// This variable will be set as the view controller so that
// the keyboard can send messages to the view controller.
weak var delegate: KeyboardDelegate?
// MARK:- keyboard initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initializeSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
initializeSubviews()
}
func initializeSubviews() {
let xibFileName = "Keyboard" // xib extention not included
let view = NSBundle.mainBundle().loadNibNamed(xibFileName, owner: self, options: nil)[0] as! UIView
self.addSubview(view)
view.frame = self.bounds
}
// MARK:- Button actions from .xib file
#IBAction func keyTapped(sender: UIButton) {
// When a button is tapped, send that information to the
// delegate (ie, the view controller)
self.delegate?.keyWasTapped(sender.titleLabel!.text!) // could alternatively send a tag value
}
#IBAction func backspace(sender: UIButton) {
self.delegate?.backspace()
}
#IBAction func Done(sender: UIButton) {
self.delegate?.keyDone()
}
}
In the viewController I'm pretty sure I've included all the necessary things to access the keyboard seeing as it works for some textFields. Such as:
class myViewController: UITableViewController,UITextFieldDelegate, KeyboardDelegate
Then in viewDidLoad set each textField delegate:
self.textField1.delegate = self
self.textField2.delegate = self
self.textField3.delegate = self
// initialize custom keyboard
let keyboardView = keyboard(frame: CGRect(x: 0, y: 0, width: 0, height: numpad.height))
keyboardView.delegate = self // the view controller will be notified by the keyboard whenever a key is tapped
// replace system keyboard with custom keyboard
textField1.inputView = keyboardView
textField2.inputView = keyboardView
textField3.inputView = keyboardView
Then this function (which seems to me to be the problem):
func keyDone() {
view.endEditing(true)
//activeTextField.resignFirstResponder()
print("please dont freeze")
}
I have checked all the connections, they seem to be fine.
Let me know if I can add any more info to help work it out.
Many Thanks.
Solved!!!
I suppose ill just put it down to beating my head over it rather than taking a break from the screen! Still I'm confused why it wasn't given a more specific error.
The problem was that in some cases one of the functions was dividing by zero (this is undefined... not possible) but a good thing to take from this(thank you Olivier) is the Instruments Tools to help find where abouts the code was losing its mind. This tutorial helped me understand how to use instruments! So once I could see where it was going crazy I set up a bunch of print statements to watch the values as they went into the 'problem' calculation, where I found the denominator to be zero. Bit of rearranging the code around to avoid this and problem solved!
This error message is basically saying that there is a memory issue, try running the code with instruments (Allocations in particular) this might reveal is there is something amiss with your keyboard
Edit 2: for anyone finding this error message in future (actual solution in this case)
Double check any code code running after keyDone() to see if there are any infinite loops or situations that would cause the compiler to assume an infinite amount of memory is required. In this case a line of code was dividing by zero, causing a fatal memory error (unable to allocate the N/A value it generated)
I am newbie in swift and I am building an app that similar to vine. First I open my app I am following the codes as you seen in the below
videoArray.removeAll()
let url = NSURL(string: "http://....")!
self.videoArray.removeAll()
Mellon.getExploreVideos(url,cache: cache, completionHandler: { (data, response, error) -> () in
dispatch_async(dispatch_get_main_queue()){
self.videoArray = data!
self.tableView.reloadData()
}
})
override func viewDidDisappear(animated: Bool) {
cache?.removeAllObjects()
videoArray.removeAll()
tableView.reloadData()
NSNotificationCenter.defaultCenter().removeObserver(self.tableView)
print("siliniyor")
}
In every 8 cell I am calling that getExplore func. I have about 80 videos in my tableview cell in my custom view cell I am building an AVURLAsset and AVplayer. I also set objects for cache in getExplore and when I close view controller I am removing all of them but as you seen from the picture all memory is not removing in every time I scroll tableview begin to end(I have 80 videos that tableView) and I change to view controller and back again I have 20 mb surplus in memory and at the end my app crush. What should I do for this problem? If the informations are not enough I can edit.
In your viewDidDisappear, make sure you call on to the super.viewDidDisappear as well. To quote the documentation-
You can override this method to perform additional tasks associated
with dismissing or hiding the view. If you override this method, you
must call super at some point in your implementation.
This might ease your memory issues. Give it a try.
I know this is a crazy asked question, but I still can't figure it out. I am using a library for creating a circular progress bar (KDCircularProgress). Everything works fine, but the moment I try to call and perform a segue (for changing the ViewController), the app crashes with the "optional value" error.
class ViewController: UIViewController, UIGestureRecognizerDelegate {
...
// Gauge pressure
var frontGaugeProgressLeft: KDCircularProgress! //define the variable here
...
override func viewDidLoad() {
super.viewDidLoad()
...
frontGaugeProgressLeft = KDCircularProgress(frame: CGRect(x: 0, y: 0, width: 125, height: 125))
frontGaugeProgressLeft. ... //do some settings here
// And display the progress bar to the view
view.addSubview(frontGaugeProgressLeft) //if this is commented, app works
...
}
...
// Show settings/preferences menu
func panAction(sender: UIScreenEdgePanGestureRecognizer) {
if sender.edges == UIRectEdge.Right {
performSegueWithIdentifier("showMenuSegue", sender: nil)
}
}
...
}
When I swipe left and trigger the segue, for changing the ViewController, the app crashes. I narrowed it down to that view.addSubview line. If I comment the line, app works. Tried some things, but still no clue how to fix this. Appreciate the support!
Fixed. There was some method in the library. Commented that since it was not used, everything alright.
// public override func didMoveToWindow() {
// progressLayer.contentsScale = window!.screen.scale
// }