UIWebView not won't "scrollsToTop" on iOS 7? - ios

I have two UIWebView in a single ViewController in order to preload one in the background for the "next page".
Since opening the project in Xcode 5, these no longer scroll to the top when the status bar is tapped.
The App is otherwise working fine with iOS 7 so I don't know what's wrong. When one of the UIWebView is hidden, I also setScrollsToTop:NO ... so there's clearly something I'm missing.
Any ideas?

I've written a simple class for this specific issue. In my app we're having multiple webviews and scrollviews. This makes it all much easier.
https://gist.github.com/hfossli/6776203

Use this category:
UIView+LogViewHierarchy.h
#import <UIKit/UIKit.h>
#interface UIView (LogViewHierarchy)
-(void) logViewHierarchy;
#end
UIView+LogViewHierarchy.m
#import "UIView+LogViewHierarchy.h"
#implementation UIView (LogViewHierarchy)
-(void) logViewHierarchy
{
NSLog(#"%#%#", NSStringFromClass(self.class), NSStringFromCGRect(self.frame));
[self logSubviews:self depth:1];
}
-(void) logSubviews:(UIView*)view depth:(NSInteger)depth
{
for (UIView *subview in view.subviews) {
if ([subview isKindOfClass:[UIScrollView class]]) {
NSLog(#"%#%#%# - scrollsToTop: %#", [self paddingString:depth], NSStringFromClass(subview.class), NSStringFromCGRect(subview.frame), ((UIScrollView*)subview).scrollsToTop ? #"YES" : #"NO");
} else {
NSLog(#"%#%#%#", [self paddingString:depth], NSStringFromClass(subview.class), NSStringFromCGRect(subview.frame));
}
[self logSubviews:subview depth:depth+1];
}
}
-(NSString*) paddingString:(NSInteger)depth
{
return [#"" stringByPaddingToLength:depth*2 withString:#" " startingAtIndex:0];
}
#end
Then find your app's root view controller and add:
-(void) viewDidAppear:(BOOL)animated
{
[self.view logViewHierarchy];
}
You will be able to see all UIScrollViews and their scrollsToTop properties. When you manage to make your output look like there is only 1 UIScrollView that has scrollsToTop == YES, then your scrollsToTop gesture should work fine.
In my case, logViewHierarchy reported that all your scroll views have scrollsToTop = NO, and you didn't know why, because I explicitly set them to YES.
It turned out that I left this UIScrollView category inside my project, and it overloaded some methods on all my UIScrollViews. When I removed that category from my project, I was able to get correct output from logViewHierarchy. Then I played around with code until I managed to get only one scrollsToTop = YES.

Related

Layout views below another programmatically created views

I have a screen, which contains multiple UIImages (their amount and size are known only at runtime, so i add them programmatically) and some fixed buttons below these UIImages.
How to make buttons display certainly under all Images?
I've tried
1.) Put Buttons and Images into 2 separate views, and then add constraint between them. No result, buttons are hidden behind images.
2.) Put buttons into separate view and set constraint in code, (tried both viewDidLoad and viewDidAppear). Constraint is set between container view and top of the screen, depending on size and amount of images.
Example of code:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSInteger totalImages = [self.object.fullphotos count];
self.labelsTopConstraint.constant = totalImages*(imageHeight + 20) + 10;
}
In case 2 buttons are positioned right, but don't respond to touches.
How should I layout everything correctly?
Thanks in advance!
Take a Tableview for those images and add buttons in a last cell.
The best way is creating a Object with a refresh method that can be called in viewDidAppear
MyObject.h
#interface MyObject : UIViewController
#property (nonatomic,strong) UIImageview *img;
#property (nonatomic,strong) UIButton *btn;
- (void) refresh;
in MyObject.m
- (void)viewDidLoad {
[self.btn addTarget:self action:#selector(myMethod:) forControlEvents:UIControlEventTouchUpInside];
}
- (void) refresh {
//make your settings here
}
-(void)myMethod {
//your button action here
}
Then in your controller if you have your objects in an NSArray:
-(void) viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
for (MyObject *myObj in objectsArray) {
#autoreleasePool {
[myObj refresh];
}
}
}

Dismiss the keyboard with MULTIPLE UITextFields?

Is it possible to dismiss the keyboard when you have MULTIPLE UITextFields ? If so how ?
As a side note, do I have to dismiss the keyboard for Each and Every field or can it be done globally ? Oh and it would be super cool if I don't have to touch the DONE button, I'd ideally like a solution that where the user touches anything BUT the field in question and the keyboard automagically disappears...
Oh and if you'd be so kind step by step instructions.
I should have added that I have a method already to resign the keyboard....
However, it only runs when my form is submitted! (see method below)
My question is how to the keyboard to hide/dismiss without having to jump thru so many damned hoops! You'd figure after 6 years, a mature operating system would have a way to GLOBALLY hide the keyboard....NOT!
Ok, enough whining....
- (void)hideKeyboard {
[self.dancePlace resignFirstResponder];
[self.danceGate resignFirstResponder];
[self.danceTerminal resignFirstResponder];
[self.danceText resignFirstResponder];
[self.danceDate resignFirstResponder];
[self.danceStyle resignFirstResponder];
[self.danceTimeOut resignFirstResponder];
}
And this is called when my button is submitted....
- (IBAction)addListingPressed:(id)sender {
// NSLog(#"BUTTON PRESSED");
[self hideKeyboard];
[self valuesAdded];
}
My question, assuming anyone can answer this...and I suspect not, is there a way to globally hide the keyboard if the following conditions are MET: 1.) the user taps OUT of any one of the existing fields, 2.) presses anywhere else on the screen. 3.) Is no more than a line or two in the existing viewcontroller.m file. 4.) I don't have to add a confusing button on the viewcontroller. (any time I have to add outlets, the damned thing is crashing on me...and then nastiness happens, and really...remember I am JUST a beginner, and its very confusing to read that I have to place this here and that there...oy. Simple folks, simple. I'm not looking for elegant solution, just so that it works.
I have a super class that all my view controllers inherit from. In that class I have this code.
MySuperViewController.h
#import <UIKit/UIKit.h>
#interface MySuperViewController : UIViewController
#property(strong, nonatomic) UITapGestureRecognizer *backgroundTapGestureRecognizer;
#end
MySuperViewController.m
- (void)viewDidLoad{
//add a tap gesture recognizer to capture all tap events
//this will include tap events when a user clicks off of a textfield
self.backgroundTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(onBackgroundTap:)];
self.backgroundTapGestureRecognizer.numberOfTapsRequired = 1;
self.backgroundTapGestureRecognizer.cancelsTouchesInView = NO;
[self.view addGestureRecognizer:self.backgroundTapGestureRecognizer];
}
- (void)onBackgroundTap:(id)sender{
//when the tap gesture recognizer gets an event, it calls endEditing on the view controller's view
//this should dismiss the keyboard
[[self view] endEditing:YES];
}
I have the UITapGestureRecognizer as a public property, so I can override it if I need to.
subclass
MyViewController.h
#import <UIKit/UIKit.h>
#import "MySuperViewController.h"
#interface MyViewController : MySuperViewController<UIGestureRecognizerDelegate>
#end
MyViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
//You don't always want the keyboard to be dismissed, so you tie into the gesture recognizer's delegate method
//By doing this, you can stop the endEditing call from being made
[self.backgroundTapGestureRecognizer setDelegate:self];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
//touch.view is the view that recieved the touch
//if this view is another textfield or maybe a button, you can return NO and the endEditing call won't be made
if (touch.view == self.myViewThatShouldNotBeBlocked) {
return NO;
}
//if you want the gesture recognizer to accept the event, return yest
return YES;
}
I uploaded an example project to github.
https://github.com/JeffRegan/KeyboardBeGone
RDVKeyboardAvoiding is a scroll view with a tap gesture recognizer, designed for multiple textViews/textFields. It keeps track of the active view and removes a lot of boilerplate code.
tap anywhere outside the textField .. it will hide it..
[self.view endEditing:YES];
There are couple of other ways to do it.
[myEditField resignFirstResponder];
[myEditField endEditing];
[parentView endEditing];
If you dont wont to do so many things and simply want to dismiss keyboard than give iboutlet to each of your text filed to following method..
-(IBAction)hidekeyboard:(id)sender
{
[sender resignFirstResponder];
}
Yes, you only have to dismiss it for the one that is currently being edited.
In order to know which one is being edited, you can check the -(BOOL)isFirstResponder property, which will return YES if it is the first responder (the one being edited) or NO if it is not. Once you know which one is the first responder you can call -(void)resignFirstResponder on that one to get rid of the keyboard.
For example, if you have a method called -(void)aMethod that you want to dismiss the current view controller and you have an array of textViews called textArray, you could do a little loop such as:
-(void)aMethod {
for (UITextField *text in self.textArray) {
if ([text isFirstResponder]) [text resignFirstResponder];
return;
}
}
This way, you can have a variable number of textFields and it will still work.
If you only have one or two textFields and you do not want to create an Array object, you could do (assuming the fields are named text1 and text2:
-(void)aMethod {
if ([text1 isFirstResponder]) [text1 resignFirstResponder];
else if([text2 isFirstResponder]) [text2 resignFirstResponder];
}
Also, to make things easier for the future you could create a category method for UIView (which is what I do) to get the current first responder if it exists as a subview of that view:
#implementation UIView (GetFirstResponder)
- (UIView *)getFirstResponder {
if ([self isFirstResponder]) return self;
else {
for (UIView *subview in self.subviews) {
UIView *firstResponder = [subview getFirstResponder];
if (firstResponder) return firstResponder;
}
}
return nil;
}
You can put this method on the top of any file that you want to call it from, or create a separate file for it and import it.
Once you have this method, you can call:
- (void)aMethod {
UIView *view = [self.view getFirstResponder];
if (view) [view resignFirstResponder];
}
[superview endEditing:YES]; // superview can be the view controller's view property.

Check if an UIImageView is on subview

In my app I need to call some UIViews more than once. But in one of my method, i've a code like :
[self addSubview:UIImageView];
But i've read that addsubview method must be call once. So, to let the code how is it, how could I check if it's already on subview ? Like :
if ([UIImageView isOnSubview] == NO)
{
[self addSubview:UIImageView];
}
Because I don't find any method to check this :/
Thank you !
You are probably looking for UIView's -(BOOL)isDescendantOfView:(UIView *)view; taken in UIView class reference.
use this one
for (UIView *subview in [self subviews])
{
NSLog(#"%#", subview);
// ---------- remember one thing there should be one imageview ------
if(![subview isKindOfClass:[UIImageView class]])
{
[self addSubview:UIImageView];
}
}

Showing a "what's this?" message when a UITableViewController is blank

My app contains several UITableViewControllers which don't necessarily have content in all circumstances. For example, a Drafts screen is empty if the user doesn't have any drafts.
In cases like that, I'd like to show a brief message explaining what the screen is for, something like this screen from the built-in Photos app:
What is the best way to get a descriptive view on screen? I can't subclass UIViewController directly, as I'm depending on some iOS 6 functionality that's tied specifically to UITableViewController, so as far as I can tell, I have to display this view inside a UITableView. Any suggestions?
Subclass UITableViewController, and then in -viewDidAppear: or some other similarly appropriate place, check if the number of cells in the table will be zero. If so, add this overlay; if not, ensure the overlay is removed. Sample code below:
#interface MyTableViewController : UITableViewController
...
#property (nonatomic, weak) UIImageView *informativeOverlayImageView;
...
#end
#implementation MyTableViewController
...
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Just for an example - you'll have your own logic for determining if there will be zero rows.
if (self.myDataModel.items.count == 0 &&
!self.informativeOverlayImageView.superview)
{
if (!self.informativeOverlayImageView)
{
self.informativeOverlayImageView = [[UIImageView alloc] initwithImage:[UIImage imageNamed:#"someImageName"]];
[self.informativeOverlayImageView sizeToFit];
}
[self.view addSubview:self.informativeOverlayImageView];
}
else if (self.myDataModel.items.count > 0 &&
self.informativeOverlayImageView.superview)
{
[self.informativeOverlayImageView removeFromSuperview];
[self.tableView reloadData]; // Add animations to taste.
}
}
...
#end
Hope this helps!

iOS get which uielement invoked UIKeyboardWillShowNotification

In my iOS app I have several UIElements that can process user input: textfields, editable webviews, etc. each time I write something into these UIElements the keyboard (obviously) will come up. Before it happens I can catch this event by observing the UIKeyboardWillShowNotification.
I would like to know what's the way to find out which UIElement invoked this action.
Thanks for your help!
The keyboard is launched when the view tapped by a user is set as FirstResponder, so I think this question is the equivalent of saying how do I get the current first responder when UIKeyboardWillShowNotification is received?.
The answer to that question by Thomas Muller was to use a class extension along the lines of:
#implementation UIView (FindFirstResponder)
- (UIView *)findFirstResponder
{
if (self.isFirstResponder) {
return self;
}
for (UIView *subView in self.subviews) {
UIView *firstResponder = [subView findFirstResponder];
if (firstResponder != nil) {
return firstResponder;
}
}
return nil;
}
#end
So I think you could use that inside your handler for UIKeyboardWillShow to figure out what caused it.
I think the section 4 (Moving Content That Is Located Under the Keyboard) of this document can give you a hint about knowing wich element has the keyboard.
http://developer.apple.com/library/ios/#DOCUMENTATION/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW1

Resources