UIBarButtonItem - Invalid tap glow size - iphone

I have a strange problem with glow that appears when pressing on a UIBarButtonItem in the UIToolbar control. When I set ImageInsets property to 4,0,0,0 the glow gets smaller every time I tap on it. Here is an illustration:
The problem doesn't appear if I don't set imageInsets. The problem appears for all buttons in the UIToolbar. I don't have tap handlers. Making bigger inset (e.g. 8,0,0,0) produces the same result faster.
I appreciate any suggestions on how to solve the problem.
EDIT: Changed the code to Objective-C since the problem reproduces without MonoTouch as well.
It's default single view project. I added a toolbar and a UIBarButtonItem into it using storyboard designer. Created an outlet for the button.
#import "ViewController.h"
#implementation ViewController
#synthesize testBtn;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
UIEdgeInsets insets = UIEdgeInsetsMake(8, 0, 0, 0);
[testBtn setImageInsets:insets];
}
- (void)viewDidUnload
{
[self setTestBtn:nil];
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end

It is hard to see what the problem is without code. Would you mind posting the relevant parts of your code up? I am thinking it has something to do with you repeatedly incrementing the top inset by 4... I will edit my answer once I see the relevant code.
EDIT: So I am not familiar with Monotouch... and from the code you pasted I am assuming the constructor does indeed get called once. I am thinking there may be a bug in the Monotouch framework which is causing the imageinsets to be shifted by the specified amount (down 4) every time the button is tapped. I would check in the following order:
That the delegate method tied to the button is not pointing to the constructor.
Change the inset to (8,0,0,0) and in 5 taps do you see the same image as above (10x). If so it may be an issue with the monotouch framework or with how you hooked up your delegate method.
Sorry but I am unfamiliar with monotouch so I can't provide much more assistance. Unless you are locked into using Monotouch, I would strongly recommend you learn native Objective-C and program natively to avoid small pitfalls and headaches such as this. Objective-C and the iOS SDK is pretty elegant.

Finally, I asked designer to adjust image size and thus get rid of the imageSize property use.

Related

UITableView not scrolling after switching to iOS 7

In iOS 6, I had a UITableView created using QuickDialog in my app. It scrolled normally. When I switched to iOS 7, the same UITableView does not scroll properly. I can drag to the bottom (the scroller compresses) but when I release, it pops back up the to the top. I've been playing with viewDidAppear to try and diagnose the problem. See the code block below.
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"Content height: %f",self.quickDialogTableView.contentSize.height);
[self.quickDialogTableView reloadData];
NSLog(#"Content height: %f",self.quickDialogTableView.contentSize.height);
[self.quickDialogTableView layoutIfNeeded];
NSLog(#"Content height: %f",self.quickDialogTableView.contentSize.height);
}
The output of this block in iOS 7 is
Content height: 0.000000
Content height: 836.000000
Content height: 0.000000
Meanwhile, the output of this block in iOS 6 (simulator) is
Content height: 836.000000
Content height: 836.000000
Content height: 836.000000
Also to try and diagnose the problem, I set up a button that would trigger [self.quickDialogTableView reloadData]. Whenever that button is pushed, the scrolling behavior begins to function normally. Then when I leave the view and come back, the scrolling fails again (until the button is pushed). To be clear, I have tried to put a reloadData in viewWillAppear by itself (i.e., removing the last two lines in the code block above) and it does not correct the scrolling.
I'm looking for clues as to where I might look to correct the issue. Thanks in advance for any help.
Okay, so I couldn't figure out the source of the problem but I did find a workaround that I hope helps someone else. Or at least, maybe helps someone else point out what's really wrong.
I created a property called trueContentSize where I store what the correct size is.
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.quickDialogTableView reloadData]; // Calculates correct contentSize
self.trueContentSize = self.quickDialogTableView.contentSize;
}
Then, in -viewDidLayoutSubviews I correct the contentSize manually.
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.quickDialogTableView.contentSize = self.trueContentSize;
}
Just thought I might throw in my two cents here..
Same problem was happening to me. Table loads fine at first, but navigate to a different screen, come back, and the table view's contentSize is 0,0 and unscrollable. Fausto's workaround wasn't reliably working, either.
What turned out to be the case for me was just the act of referencing topLayoutGuide in -viewWillLayoutSubviews. Doing that causes all of the above symptoms. Try it out in a new project:
Setup a table view controller inside a tab controller.
Give it 30 rows with just static content.
Run and you can scroll. Switch tabs, then back, and you can scroll.
Add NSLog(#"%#", self.topLayoutGuide); in -viewWillLayoutSubviews
Run again, you can scroll, but switch tabs, no more scrolling.
Weird. Sounds like an iOS bug, or you just shouldn't reference topLayoutGuide in that method. Removing any reference to that property will fix the issue.
Edit: As of earlier this year, Apple confirmed via a Radar report I made this is an iOS bug. Don't reference topLayoutGuide until it's been resolved (whenever that will be, heh). :)
This is my current temporary fix. Anyone figure out the real fix?
#interface UITableView (Extension)
#end
#implementation UITableView (Extension)
- (void)setContentSize:(CGSize)contentSize {
if (contentSize.height != 0) {
[super setContentSize:contentSize];
NSLog(#"set content height %f", contentSize.height);
} else {
NSLog(#"set content size zero");
}
}
#end
Faced the same problem and the only solution found was to reload the data in the table. Hope that helps.
I had a similar issue. My issue was that I had an explicit height set for the table view in the storyboard. Updating my table view to make use auto layout by setting distance to the views above / below made it work for me.

App is behaving different on iPhone 5.1 simulator and real iPhone 4 with iOS 5.1

In a nutshell:
When the title of a back-button of a navigation controller gets changed, in some cases the old title is stuck and the new title will not be displayed. This happens only in some reproduceable situations, while it works as designed in other situations.
It depends on the hardware
The error happens on iPhone 3G (iOS 4.2.1) and in the simulator (iOS 5.1)
With identical sourcecode there is no error on iPhone 4 (iOS 5.1)
It depends on the word that is written to the title
When the button is created, and it gets from my selfwritten creating-method the same word as title that it would have got automatically (i.e. the title of the previous page on the navigation controller's stack), and when the other circumstances match, then, when trying to change the button's title at a later moment, the old text is stuck and the new title will not show up.
When at creation time the button gets a word as title that is different from its default-title, then every later changes of its title works fine, as long as you don't assign the default-title to it.
If, after a lot of successfull changes with many different titles, you put the word on the buttons title, that was its default title, then this word is stuck. Later changes will not be accepted (without any message, and only if the other circumstances match)
It depends on whether the button was invisible in the meantime or not.
If another view was pushed on the navigation controllers stack, so that the old page with the flawed button became hidden by the new page, and when the new page was popped from the stack later again which makes the button visible again, (and when the other circumstances match) then the old text was stuck and the trial to change it is ignored (without any message).
If the button was newer hidden, changing its title never is no problem. I works always.
The correct title is visible during animation
When the attempt to change the back-button's title was ignored due to the combination of the circumstances described above, the proper title anyhow becomes visible for about 0.3 seconds when this back-button is hit and the page's slide-to-right-animation is processed. At the beginning of the animation the old stuck title is replaced by the proper title, and the correct title is visible during the animation.
Detailed description
It's about the text on a UINavigationController's back button. I change this buttons title in dependence of new language-settings. At the moment my app has a maximum of 3 view controllers in the navigation controllers stack. Each of them is a different subclass of `UITableViewController.
Table 1, named GeneralTableVC is the root view on the stack. It has no back button. It gives the user a summary of what he has stored inside the app and it displayes a toolbar with a settings-button.
It is the navitation controller who provides this Toolbar that is visible in Table 1. It is set to invisible in Table 2 and 3. At the moment there is only one button in that toolbar named "Settings". Touching this Settings-Button will push Table 2 onto the stack.
Table 2, named SettingsTabVC has a back button, and this is the one that makes problems in the simulator but works fine on my real iPhone 4 running iOS 5.1.
By touching the first row of Table 2 a new Table (Table 3) will be created and pushed onto the stack.
Table 3, named LangSelectTableVC also has a back button, but this one works pretty fine in both devices, iPhone simulator and real iPhone 4.
Table 3 is a language selection table that displayes a list of all available languages (at the moment just english and german). Touching a row changes settings immediately. The active view (Table 3) will be redrawn, and within a few milliseconds all texts on screen appear in the new language.
Redrawing the table itself is no problem, as well as the title in the navigation bar. But the text on the back button must be translated too, and this is a little bit tricky. I have done the very same trick on both back-buttons, and it works fine for the button visible on Table 3 who is directing to Table 2. But with the very same code there is a problem in the simulator (but not on a real iPhone) with the button on Table 2 who is directing to Table 1.
I give you some code-snippets and some screenshots to show you what I've done and what is happening:
Sourcecode
ARC (automatic reference counting) is in use.
I did define a redraw-Protocol:
Protocols.h
#ifndef ToDo_Project_Protocols_h
#define ToDo_Project_Protocols_h
#protocol redrawProt
- (void) mustRedraw;
#end
#endif
This is the header of Table 1:
GeneralTableVC.h
#import <UIKit/UIKit.h>
#import "Protocols.h"
// some other imports
#interface GeneralTabVC : UITableViewController <redrawProt>
#property id<redrawProt> parent;
#property Boolean mustRedrawMyself;
#property NSString* backTitle;
#property UIBarButtonItem* myBackButton;
#property UIBarButtonItem* parBackButton;
- (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem*)bB;
#end
The header files of the other Tables, SettingsTabVC.h and LangSelectTabVC.h define the same properties and an identical init-function
The program starts here:
part of AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// some code
GeneralTabVC* genTabCon = [[GeneralTabVC alloc] initWithParent:nil andBackTitle:nil andBackButton:nil];
UINavigationController* navCon = [[UINavigationController alloc] initWithRootViewController:genTabCon];
// some other code
}
Next comes the implementation of Table 1 (GeneralTableVC.m). The Code in Table 2 (SettingsTabVC.m) and Table 3 (LangSelectTabVC.m) is analogously the same. I don't show those parts of code that implements the protocol UITableViewDataSource. I think those parts are not really important for explaining the problem.
In this code you will find the macro LocalizedString(keyword) which does exactly the same as NSLocalizedString(keyword,comment), which is translating the keyword into the desired language. My version of this macro uses a different bundel for translation (not the main bundle)
GeneralTableVC.m
#import "GeneralTabVC.h"
#import "SettingsTabVC.h"
#define MYTITLE #"summary"
id<redrawProt> parent;
Boolean mustRedrawMyself;
NSString* backTitle;
UIBarButtonItem* myBackButton;
UIBarButtonItem* parBackButton;
#interface GeneralTabVC ()
#end
#implementation GeneralTabVC
#synthesize parent, mustRedrawMyself, backTitle, myBackButton, parBackButton;
- (void) mustRedraw {
self.mustRedrawMyself = YES;
}
- (void) redraw {
if ((self.parBackButton) && (self.backTitle)) {
// Important!
// here I change the back buttons title!
self.parBackButton.title = LocalizedString(self.backTitle);
}
if (self.parent) {
[self.parent mustRedraw];
}
self.title = LocalizedString(MYTITLE);
[self.tableView reloadData];
self.mustRedrawMyself = NO;
}
- (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem *)bB {
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
self.parent = par;
self.mustRedrawMyself = NO;
self.backTitle = bT;
self.parBackButton = bB;
}
return self;
}
- (void) toolbarInit {
// this method exists only in Table 1, not in other tables
// it creates a UIBarButtonItem, adds it to self.toolbarItems
// and makes it visible
}
- (void)SettingsAction:(id)sender {
// this method exists only in Table 1, not in other tables
// it will be executed after the user tabs on the settings-
// button in the toolbar
SettingsTabVC* setTabCon = [[SettingsTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton];
[self.navigationController pushViewController:setTabCon animated:YES];
}
- (void) viewDidLoad {
[super viewDidLoad];
self.title = LocalizedString(MYTITLE);
// I want an Edit-Button. Localization of this button is
// not yet done. At the moment is uses the systems language,
// not the apps language.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self toolbarInit];
}
- (void) viewWillAppear:(BOOL)animated {
// this is an important method! Maybe here is the reason for
// my problem!
[super viewWillAppear:animated];
// When ever this controllers view is going to appear, and
// when ever it is necessary to redraw it in a new language,
// it will redraw itself:
if (self.mustRedrawMyself) {
[self redraw];
}
// And here comes the buggy back button:
// When ever this controllers view is going to appear,
// a new back button will be created with a title in the
// new language:
UIBarButtonItem* BB = [[UIBarButtonItem alloc] init];
BB.title = LocalizedString(MYTITLE);
self.myBackButton = BB;
self.navigationItem.backBarButtonItem = self.myBackButton;
}
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// show toolbar:
[self.navigationController setToolbarHidden:NO animated:YES];
}
// next methods are about InterfaceOrientation and the
// UITableViewDataSource protocoll. They are not important
// for the problem.
// but maybe the very last method is important. It comes in
// different versions in the three implementation files:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// This is the version of GeneralTableVC.m (Table 1)
// It does nothing (at the actual stage of expansion, in later
// versions it will start the main business logic of this app)
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// This is the version of SettingsTableVC.m (Table 2)
// Tabbing onto row 0 of section 0 will push the
// language-selection-table (Table 3) on screen:
if (indexPath.section == 0) {
if (indexPath.row == 0) {
// create Table 3:
LangSelectTabVC* langTabCon = [[LangSelectTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton];
[self.navigationController pushViewController:langTabCon animated:YES];
} else {
// do something else (nothing at this stage of expansion)
}
} else {
// do something else (nothing at this stage of expansion)
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// This is the version of LangSelectTableVC.m (Table 3)
// here I do some magic to select and store the new language.
// Part of this magic is transforming indexPath.row
// into a valid language-code, putting it into the
// settings-object, and registering this object to
// NSUserDefaults
}
#end
Screenshots
Starting the app on iPhone 5.1 simulator will put Table 1 (GeneralTableVC) on screen:
In the toolbar on the screens button, on its right side, you find a settings-button. Pressing this button brings the next table on screen:
Watch the back button in the title bar. It displays the text "Summary", which is correct, since the previous table title was "Summary".
Now we tab onto the first row ("Language English >"):
Everything is fine.
Now let's change the language. Tab on "German":
Wow! Everything is in German now. Even the back button has changed from "Settings" to "Einstellungen".
Lets tab on that "Einstellungen" back button:
Almost everthing is fine now; everything has changed into german. Everything but the back button, which still says "Summary" instead of "Überblick". And I do not understand why, because when I do exactly the same steps with exactly the same sourcecode on my real iPhone 4, the last screen looks like this:
Mind the text on the back-button. On a real iPhone 4 it is the german word "Überblick" (which is what I want), but in the simulator it is the english word "Summary".
And this means to me, on some phones (like my iPhone 4) the user gets what is expected, but maybe on some other phones (maybe iPhone 4S) the user gets a buggy display.
Has anybody an idea what is wrong with my code?
EDITs
Edit: 2012-04-06 09:04 +02:00 (central european summer time)
I did manage to test my app on an other piece of hardware, an old iPhone 3G (iOS 4.2.1) On the old iPhone my app is behaving exactly the same way like in the simulator. Running the same app on iPhone 4 produces a different behaviour.
To be more precise:
On iPhone 4 (iOS 5.1): App is doing what I want, no faulty behavior.
On Simulator (iOS 5.1): App displays wrong title on a navigation controllers back button.
On iPhone 3G (iOS 4.2.1): App shows the same faulty behaviour as in the simulator.
Edit: 2012-04-07 10:14 +02:00 (central european summer time)
By watching the transition on the iPhone 3G, I fond out something interesting and maybe helpfull: When I tab on the button with the wrong text, the following happens:
The wrong text is replaced by the correct text
After this replacement the view dissapears animated (sliding to the right) and the underlaying view becomes visible. This transition has a duration of aproximately 0.3 seconds, and in this short interval the correct text is visible in all hardware-iPhones and in the simulator too.
But the question still is: Why is the wront text displayed in iPhone 3G and Simulator? Why is the correct text always visible in iPhone 4?
To me it looks, as if there was two buttons at the same place, one over the other. In iPhone 4 "my" custom button is in front, hiding the old systemgenerated button, but in the simulator and in iPhone 3G the old systemgenerated button is in front, hiding my custom button. But: Even if my hidden custom button is bigger (wider) than the systemgenerated, nothing of it is visible. Only when the slide-out animation starts, my button becomes visible.
Edit: 2012-04-07 16:38 +02:00 (central european summer time)
Next interesting fact:
This is what happened until now:
When the button appers for the first time (2nd screenshot, see below), I put a word as title on it, that is identic to the word it would have become before from the system. Then the user selects some action, and this button is hidden by another view. After another user-action the button is revealed again, it now it shall get a new word as title (same meaning, but new language), but on iPhone 3G and on the simulator the old title is "stronger". The new title will not be displayed. The old title sticks there.
This does not happen if at first appearence I write a word as title onto the button, that is different from the systemgenerated title. If the first title is different from the default-title, a later change will be executed on all iPhones and on the simulator.
That makes me believe, that iOS does some kind of "optimization": If, at first appearance of the button, the custom title is identic to the systemgenerated title, than a later change of the buttons title will be ignored, but only on iPhone 3G and simulator. On iPhone 4 a later change will be allowed in any case.
But setting a different title at the beginning to prevent the app from its faulty behaviour is not an option.
I suspect the issues you're seeing are down to subtle timing issues between the sequence that occurs on the Simulator, and real hardware.
The view controls are not necessarily instantiated in viewDidLoad, so you should wait until viewWillAppear to set title values (etc).
The following is meant constructively, please take it in the spirit it's intended:
Without reviewing your code in detail, I suspect what you are trying to achieve could be achieved more deterministically. What you're trying to do isn't hard or unusual, but I'm afraid your code looks unnecessarily convoluted - possibly as a result of you trying to fix these timing issues.
Take a look at some simple examples and tutorials, and look to simplify your code so it doesn't use flags to track state (mustRedrawMyself) as this shouldn't be necessary. Remember to not set properties of views / controls until viewWillAppear, and see how you get on.
You might also want to look at the inbuilt support for localisation, if you're not already.
Good luck.
I would Try this:
Move the SELF REDRAW if and execution to AFTER where you are updating it to your localization in your viewWillAppear:
You are telling the view to REDRAW before you change the title, and as such, the title update is not part of the redraw. Its doing exactly what it should.. and my guess is timing operations on some hardware/emulator are faster, so that the update you do after the call to redraw is happening before the draw completes on some hardware/emulator, but not finishing prior to the draw happening on other.
try changing the order of operations to the following, and let me know how if that works.
- (void) viewWillAppear:(BOOL)animated {
// this is an important method! Maybe here is the reason for
// my problem!
[super viewWillAppear:animated];
// And here comes the buggy back button:
// When ever this controllers view is going to appear,
// a new back button will be created with a title in the
// new language:
UIBarButtonItem* BB = [[UIBarButtonItem alloc] init];
BB.title = LocalizedString(MYTITLE);
self.myBackButton = BB;
self.navigationItem.backBarButtonItem = self.myBackButton;
// When ever this controllers view is going to appear, and
// when ever it is necessary to redraw it in a new language,
// it will redraw itself:
if (self.mustRedrawMyself) {
[self redraw];
}
}
Apple support did answer
I contacted Apple support for that issue and they did answer.
The problem was, that the navigation bar holds all back-buttons for views on the navigation controllers stack and all this buttons needs to be updated at the same time. Updating the views that lay on the stack within their viewWillAppear-Methods is good, but trying to update the back-button at this place is no good idea.
The solution:
Extend the interface of UIViewController:
#interface UIViewController (extended)
- (NSString *)localizedKey;
#end
For each UIViewController that puts a view on the UINavigationController's stack implement this method:
- (NSString*) localizedKey {
return #"a title-keyword";
}
Do not mess around with UIBarButtonItem or self.navigationItem.backBarButtonItem in any of the UIViewControllers.
When if comes that titles need to be changed, do it for ALL back buttons with this snippet of code (remember: LocalizedString(key) is a self-written macro similar to NSLocalizedString(key,comment)):
NSArray* vcs = [self.navigationController viewControllers];
for (UIViewController* vc in vcs) {
vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]);
vc.title = LocalizedString([vc localizedKey]);
}
Verbatim answer of Apple Support:
We're fighting the navigation bar in forcing an update at the wrong time. Notice all the views in each view controller get updated properly. So the navigation bar needs special attention to get what we want.
To get this to work, you need to change the back buttons to all the view controllers on the stack at once, (at the time the user selects a language) instead of when they each appear via "viewWillAppear".
This requires the ability to obtain the localized key for that button in a public way. I introduced a category to UIViewController to easily adopt this:
#interface UIViewController (extended)
- (NSString *)localizedKey;
#end
Then your LangSelectTabVC class can change all the back buttons at once. This approach will make the button titles redraw correctly.
So in viewWillAppear, you won't have to update each back button. Doing it there appears to be too late for UIKit to catch that update. You also re-create a new back button when an update occurs. This is not necessary, just take the current one and change it's title:
NSArray *vcs = [self.navigationController viewControllers];
for (UIViewController *vc in vcs)
{
vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]);
vc.title = LocalizedString([vc localizedKey]);
}
I've attached a modified project showing this workaround.

When can you set Interface Builder objects' properties?

When in the view life cycle can you change the values of objects that were put there in IB?
I have a stepper and a UIImageView that I put into the view in IB. I had put some values of them (like the stepper min and the UIImageView's image) in ViewDidLoad but when the view appears, it uses the stepper min and the image that were set in IB.
You can set the properties where you are trying.
Make sure to call [super viewDidLoad]
- (void)viewDidLoad
{
[super viewDidLoad];
// adjust views here
}
It is possible you forgot to connect your IBOutlets in IB:
This will lead to a bug that the compiler does not care about. Essentially your slider code would be equivalent to
[nil setMinimumValue:newVal];
I can run the following code in a test program and no complaints or crashes happen
UIStepper * step = nil;
step.minimumValue = 5;
You can set those values in viewDidLoad. It would be helpful if you posted your viewDidLoad source code.
Since I haven't seen your code, I can only guess at what is wrong. I suspect that your view controller has a property referencing the stepper and another property referencing the image view, and you are setting the values using those properties, like this:
- (void)viewDidLoad {
[super viewDidLoad];
self.stepper.minimumValue = minValueForStepper();
self.imageView.image = imageForImageView();
}
If your code has that basic structure and it's not working, you probably forgot to hook up those properties in your nib.
I dont understand you question very well, but if you want set your IBOutlets you can do programmatically for example, if you have a:
IBOutlet imageView *theImageView;
You can set one of UIImageView properties like that:
[theImageView setImage:[UIImage imageNamed:#"theimage.png"]];
You can study all the classes that you are interested in the Organizer view of Xcode, here you can view all of its properties, methods, delegate methods and so on.
Ah. I see the problem. I had a UILabel that was displaying the value and I didn't have it set yet to connect to the UIStepper's value until the value changed. My bad. Thanks guys!

scrollsToTop doesn't work when UIWebView used with pushViewController, looking for solution

I have an app with a table view at the root in a navigation controller, and on selection of a table cell it displays a new view controller that contains only a UIWebView (with a toolbar and a navbar).
Depending on how I present the new web view, the feature where the user can tap on the status bar at the top and have the webview scroll to the top, either works or doesn't work.
If I use:
(void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated
on the RootView, then the webview does scroll to the top.
If I change that one line of code and instead use:
(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
on the navigation controller, then the scrollsToTop feature stops working.
What I really want to use however, for other reasons in the context of the app, is the pushViewController method. BUT, I also want to keep the scrollsToTop behaviour.
I have so far tried various approaches, some described here:
-Attempting to set the webview internal scrollView scrollsToTop property
((UIScrollView *)[[webView valueForKey:#"_internal"] valueForKey:#"scroller"]).scrollsToTop = YES;
(No discernible effect).
-Changing the ordering of setting NavBar properties or not setting any at all
-Adding extra "window makeKeyAndOrderFront" calls after the new view push.
I don't believe there are other views there that could be claiming the "scrollsToTop" property (and the first test above proves that in any case).
Short of attempting to embed UIWebView into a UIScrollView, which I expect will be painful, I have run out of routes to explore to resolve this issue.
I am hoping someone else has found a way to correct this?
Try the reverse approach as a workaround.
In the root view controller:
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.tableView.scrollsToTop = NO;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.tableView.scrollsToTop = YES;
}
Bye!
It turns out that it wasn't working because of the way I was customiing the Navbar. I was overriding leftBarButtonItem with a UIBarButtonItem that then took up the whole navbar (I think I took that approach from some Apple code many months ago, but not sure). I was using the same approach on another view and the scrollsToTop worked fine there.
Taking that code out and putting the custom Navbar view into (eg)self.navigationController.titleView instead solved this problem. I do not really know why, like I say it works fine for other views.
I wasted a lot of time with this problem, so I hope I save someone else some hair by describing it here :)

How do I prevent text entry in a UITextView?

I have a text field entry in my view that I would like to block access to during a background operation. I've tried using the editable property, which successfully blocks access during the background operation, but the moment I set editable to YES, the keyboard comes up and the textfield becomes the first responder. Dismissing the keyboard just after changing editable doesn't do anything:
// Broken code
textView.editable = YES;
[textView resignFirstResponder];
I've thought about adding a clear UIView that just blocks access to the UITextView after dismissing the keyboard, but that seems like overkill. Is there a correct way to handle this?
Just so people don't have to read farther than the selected answer: It turns out that this is a "known issue" in the SDK, and you can find it listed in the release notes. Using userInteractionEnabled performs the same function, as long as you make sure to dismiss the keyboard yourself.
Try textView.userInteractionEnabled = NO;
Put a UIView in front of the UITextView with a dark (or white) background color and alpha set low (like 5%) sized to fully cover the textview. Default it to hidden.
When you want the textinput disabled, send it a resignFirstResponder then show the hidden layer on top. It intercepts user inputs (and ignores it). The alpha color will make it look 'dimmed.' Once your background operation is done just set the cover view to hidden and you're good to go. If you want to get fancy you can do UIView alpha fade animations.
I'm not sure of a "correct way" but I'd probably do the transparent view solution... I agree that it seems like overkill but the simple solution is often a good answer.
Since the view gets focus upon changing the editable properties this would be easier.
the other solution that I can think of is to derive a custom UITextView and recode the editable property (or make a new method) that can accomplish what you are trying to do. This is a good object oriented solution but this could be come cumbersome.
You might also consider using a Category to add the functionality. But for either of these solutions, are still back to square one of how to accomplish what you need...
Thank god someone came up with a better response. I originally built it with the following blocker, but the userInteractionEnabled BOOL is much easier.
It turns out that the problem is a known issue with UITextView. My workaround:
#import <UIKit/UIKit.h>
/**
God damn SDK bug means that you can't use editable to enable/disable a
UITextView (It brings up the keyboard when re-enabling)
*/
#interface StupidF_ingTextViewBlocker : UIView {
}
#end
#implementation StupidF_ingTextViewBlocker
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// Initialization code
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
Then in place of the code I've placed above (instead of using editable). To disable (assuming I have an iVar called blocker):
// Put this in a lazy loading property or in loadView
blocker = [[StupidF_ingTextViewBlocker alloc] initWithFrame:writeView.frame];
// The blocker code ~= textView.editable = NO
[self.view addSubview:blocker];
// Removing the blocker ~= textView.editable = YES
[blocker removeFromSuperView];
Subclass UITextView, and implement a single method:
- (BOOL) canBecomeFirstResponder { return NO; }
Use the UITextViewDelegate. You'll need to swap out the delegate object depending on the current state.
If in the blocked state, then you'll use a delegate where textViewShouldBeginEditing returns NO.
the other delegate's textViewShouldBeginEditing would return YES.