Synchronized access to UITableViewDelegate logic - iphone

I'd like to ask a question about table view and #synchronized contruct.
I have to execute the code inside didSelectRowAtIndexPath once, even if user keep tapping on table cell...
I have a table used as menu in a game. I'd like to provide synchronized access to the logic implemented in didSelectRowAtIndexPath.
I worte the following code:
//condition = YES in init code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
#synchronized( self)
{
if( !condition)
{
NSLog(#"multiple execution is not allowed...");
return;
}
condition = NO;
...
//code
...
}
}
I tested it on my ipone 3gs with ios 4.3.4 and worked (i made tests and the behaviour is as expected) but my client tested it on his 3g with ios 3.x installed and it seems not to work.
I thought at using GCD (by enclosing code in dispatch_once()), but it's supported starting from ios 4.x.
Have you any insight on why #synchronized doesn't work on my client's phone?
Thanks!

#synchronized should not be not needed here.
you can expect the delegate call to be made from the main thread (assuming you do not call it).
dispatch_once seems like an unusual choice here. if you are sure it is good, try pthread_once for iOS 3. it's odd because it operates using mutable global state. IOW, "I only ever want to make one table".
testing a BOOL is fast.
My guess? Your table is being removed (view unloading) and may fail to be recreated on reload.

Related

How to navigate using collectionView in UIViewController- While selecting cell it is going to random view controller not defined one

Hello,
I made this app using UITableViewController later by using buttons in UIViewController.
In UIViewController(using buttons)-the resolution either suits for iphone4s and below or iphone 5 and above..
To Overcome from this resolution issue I'm making this by inheriting collection view in Existing UIViewController...(And I embedded this to UINavigationController to navigate)
Everything works fine till displaying...Problem occurs when I select any cell, It then goes to random view controller.
I used this method
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
int cellNum = [indexPath row]+1;
switch (cellNum) {
case 1:
{
[self performSegueWithIdentifier:#"segueNew" sender:nil];
}
break;
//---So On----
}
With tableview I did the similar thing, It worked then..
If you need any further information then please leave a comment.
I really googled a lot about this but didn't find any solution.
So, Please help me on this or If you find any link please share.
Thank you.
It was an extremely silly mistake...I picked the wrong method..
I had to use this method instead of above one..
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
}

How to lock a method when using multithreading?

how can I stop 2nd thread to access same method that is being used by first thread?
One option is to use #synchronized in the method.
- (NSString *)someMethod {
#synchronized(self) {
// do some work
}
}
It allows the method to be called but will synchronize on itself and protect it's work (and more importantly the data it's working on).
Have a look at NSLock.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSLock_Class/Reference/Reference.html
Just found it when looking for a C# lock statement, it appears to do the same thing..
There is also another SO thread relating to synchronisation:
How does #synchronized lock/unlock in Objective-C?

Why calling [self becomeFirstResponder] caused so many problems?

For the past few extremely frustrating days of my life, I've been trying to figure out what's wrong with me code. In a certain page, if I put UITextViews or UITextFields or a MFMailComposer or a MessageComposer or anything with fields that require editing, the fields just wouldn't respond to touches. I couldn't edit anything when I ran the app. I couldn't edit text views or email fields or anything. I tried everything, but nothing worked. It turns out that on the main page (MainVC) that leads to the page where fields don't respond (GiftVC), in the viewDidAppear method (in the MainVC), I say: [self becomeFirstResponder];.
Now I'm not really sure why I put that there, but it turns out that commenting that line out fixes everything and makes all the fields and textviews and email composers and everything work just fine again.
I also have this in the MainVC page:
-(BOOL)canBecomeFirstResponder {
return YES;
}
and commenting that out fixes the problem as well.
The weird part is that even with the [self becomeFirstResponder] line, everything worked just fine in the new iOS 5 (simulator and device), but in iOS 4 (simulator and device), it wouldn't work at all with that line. Now that I've removed it, it works fine in both cases.
If you have the following in your UIViewController subclass
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.view.window) {
[self becomeFirstResponder];
}
}
then you probably intended to allow that subclass to handle motion events (shaking) or something similar. So that's probably why it's there.
If you weren't able to edit UITextFields then this subclass was probably becoming the first responder and not forwarding the event to the actual UITextField. When a UIViewController subclass calls overrides canBecomeFirstResponder to return YES and makes them self the first responder (ie [self becomeFirstResponder], if you want don't want that custom class to handle the touch events for the UITextField, then you should override the nextResponder method.
An example from my own product -- Essentially I have a UIViewController subclass that does two things: 1) it handles shake events and 2) it displays another view modally when some button is tapped. On the modal view there are some UITextFields. To allow my UIViewController subclass to forward the touch events to my modal view, I added the following:
- (UIResponder *)nextResponder
{
if (!self.view.window) {
// If the modal view is being displayed, forward events to it.
return self.modalViewController;
} else {
// Allow the superclass to handle event.
return [super nextResponder];
}
}
This will work on iOS 4 and 5, with either sdk.
Now, in your case you obviously didn't remember adding the code to become first responder in the first place, so you don't need the above hooks. However, it's good to know for the future.
Back to your actual question -- once you updated your SDK to 5, why wouldn't things work on iOS 4, but they would work on iOS 5? iOS 5 is doing some of the event forwarding for you which is why it works there. It should have never worked on iOS 4 in the beginning. Apple fixed some bugs that allowed it to work on 4, which is why it no longer works on 4.
I know the question had already accepted an accepted answer; I just wanted to clear up any confusion out there.
Check if MainVC has a method called canResignFirstResponder that returns NO (at least sometimes). If so, then once it becomes first responder, it won't let anything else become first responder, until it returns YES from that method. (All the UITextViews, etc. have to become first responder to be edited.)
Actually just look everywhere in all your code for canResignFirstResponder, in case it's in a superclass or something.
Otherwise the only thing that would stop the text fields and views from being editable would probably be if they got set userInteractionEnabled = NO, but since it hinges on the becomeFirstResponder statement, it is more likely to do with canResignFirstResponder.
In iOS 4, a subclass must override canBecomeFirstResponder in order to be able to become first responder. Maybe this is different for iOS 5 or it's a bug.
Try this,
Make sure you have added the uiTextViewDelegate and
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView{
NSLog(#"textViewShouldBeginEditing:");
return YES;
}

cellForRowAtIndexPath called before viewWillAppear iPhone SDK 3.2 plus

After upgrading my SDK version I noticed that cellForRowAtIndexPath is always called prior to viewWillAppear. Previously the order of execution was opposite: viewWillAppear would always be called prior to cellForRowAtIndexPath. Because my application logic often used viewWillAppear to initialize objects that are subsequently used in cellForRowAtIndexPath, the application is often crashing.
I tried searching for an official expiation regarding this change with no success. I can likely move my initialization code to viewDidLoad, but wanted to see if there are better solutions or more information about this change in behavior.
You can just add the line
[self.tableView reloadData];
at the end of viewWillAppear and your problem will be fix
It is not "cellForRowAtIndexPath called before viewWillAppear", it is [super viewWillAppear:animated] will call UITableView's delegate.
I made the same mistake and it took me a lot of time to find it.

Editing a UITextField inside a UITableViewCell fails

In my application I have a UITextField inside a UITableViewCell. If I click inside the text field and add some text I find that if try to move the insertion point it works the first time but fails on subsequent attempts. I am completely unable to move the selection; no "magnifying glass" appears.
Even more curious, this "setting" seems to be permanent until I restart the application. And it affects all UITextFields on that screen and not just the one that I originally tried to edit.
If you want to see it yourself, try the "UICatalog" sample that comes with the iPhone SDK. Click "text fields" and then "edit" and play around with the text boxes.
I've done a lot of digging on this but it's pretty hard to Google for! The best references I've found are on Apple's support board and MacRumors formum (both reference a solution that apparently used to work on iPhone 2.0 but does work not with contemporary versions -- I did try).
My feeling that is that this is a bug in the OS, but I thought I'd throw this out to the SO crowd for a second opinion and to see if there are any workarounds. Any ideas?
Following benzado's suggestion, I tried building my application using the 2.0, 2.1 and 2.2 SDKs. I got the same behaviour in all versions. (Actually, something related but not the same broke in 2.2 but that's probably another question!)
I spent a lot of time on this but I finally think that I have it nailed.
The trick is that the table needs to be editable (i.e., its editing property needs to be set to YES). The good news is that you are now able to move the insertion point. Sometimes the magnifying glass doesn't appear or follow but your gesture always seems to work.
Does this still qualify as a bug? Perhaps. At the very least Apple's SDK documentation should be updated. I've raised a bug report with Apple to cover this (rdar://6462725).
Thanks to this post, I've been able to successfully get this to work properly in my app.
Something to add, however:
If you set your table to be editable, you'll likely get different behavior than you expect (indenting, editing widgets, no disclosure indicators, etc.). This surprised me but here's how to best deal with it:
In your UITableView delegate, implement:
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
Then, in your UITableViewCell's implementation, set your UITableView to be editable ONLY when you're actually editing:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
((UITableView *)[self superview]).editing = YES;
...
}
and disable editing when editing is done:
- (void)textFieldDidEndEditing:(UITextField *)textField
{
...
((UITableView *)[self superview]).editing = YES;
}
This will ensure that your table isn't in editing mode when you're not editing the cell, keeping things working smoothly.
Thanks!
Brian M. Criscuolo
Mark/Space Inc.
The answer by Brian M. Criscuolo is the closest, however its still not quite right - in my usage of SDK2.2.1 I find that I have to do the following:
To your UITableViewDelegate (which is often your UITableViewController) add both of the following:
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
and:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewCellEditingStyleNone;
}
and:
- (void)viewDidLoad {
[super viewDidLoad];
// do any other customisation here
self.uiTableView.editing = true;
}
If you don't put the top two delegate methods, the above will cause the delete icons next to each row, and the indentation of each row.
You shouldn't need to do anything with textfield delegates as Brian indicated (unless you have multiple rows and you want to respond to a didSelectRowAtIndexPath: event - which you don't seem to get while in edit mode - then you will need to also do as he suggests).
By the way - this seems fixed in SDK3.0 (although subject to change I guess)
It does sound like an OS bug. I would try to reproduce it by running the UICatalog sample against the 2.0, 2.1, and 2.2 SDKs to see if anything changes. (There's a bug related to table cell text alignment that occurs if you build for 2.2 but not if you build for 2.1, regardless of what version of the OS is on the device.)
If it turns out to make a difference, https://bugreport.apple.com/
I tried this with my application and it seems to work as expected. I get a magnifying glass every time. I am using the 2.1 SDK.
actually what works best seems to be to set the table "editing" property to true in "viewDidLoad" and adding these to the table delegate
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
you don't need to do anything in the text field delegate
In iOS 12 or later you can select table view cell in the storyboard, then enable User Interaction Enabled in the attribute inspector.