UITabBarController Subviews Don't Appear Weirdness - objective-c

I have a curious issue with an iOS 9/ObjC app I am writing using a 4 tab format. The "main" ViewController has several subviews with controls and an image. When the user taps the image a PickerView pops up and it is dismissed with another tap on the image. The other tabbed ViewControllers lead to simple views. It was all put together with IB, except for a single subview on the "main" view where I do some Quartz animation drawing superimposed on the image view there.
If I stay on and interact with just the "main" view, all works exactly as desired. The PickerView appears and disappears as designed. Tabbing to another ViewController, then back to the "main" View leads to the problem: A tap on the image view fails to pop up the Picker. The tap is recognized in the Quartz layer, fires a Notification event which is picked up in the picked up in the main view controller which in turn adds the picker subview, brings it to the front and enables interaction just as when it works properly, but the picker never appears, even though the Debugger shows it successfully added to the subview array.
Even stranger, if I tab off the main to any other tab view, then back to main, the picker now appears again as it should. This sequence is completely repeatable - tab once off the main, and the picker doesn't visualize, tab again and then back to main and the picker works.
Any thoughts on where to start looking? Thanks
Here's some code (but I'm not sure where the problem is)
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.picker = [[UIPickerView alloc] init];
self.picker.datasource = self;
self.picker.delegate = self;
float screenWidth = [UIScreen mainScreen].bounds.size.width;
float pickerWidth = screenWidth * 3 / 4;
// Calculate the starting x coordinate.
float xPoint = screenWidth / 2 - pickerWidth / 2;
[self.Picker setFrame: CGRectMake(xPoint, 50.0f, pickerWidth, 250.0f)];
[self.Picker setClipsToBounds:NO];
[self.Picker setBackgroundColor:[UIColor whiteColor]];
self.Picker.showsSelectionIndicator = YES;
self.Picker.userInteractionEnabled = YES;
.
.
.
UIImage *base = [UIImage imageNamed:#"Base"];
self.baseAView = [[UIImageView alloc] initWithFrame:imFrame];
[self.baseView setImage:base];
[self.view addSubview:_baseView];
self.GView = [[GView alloc] initWithFrame:imFrame];
self.GView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_GView];
}
-(void) viewDidAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSin) name:#"touchP" object:nil];
}
- (void) doSin {
// user hits GView, draw Picker if not in View Hierarchy, if in Hierarch, grab value and resign
NSArray *subViews = [self.view subviews];
__block NSInteger foundIndex = NSNotFound;
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UIPickerView class]]) {
foundIndex = idx;
// stop the enumeration
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
// Found the UIPickerView in subviews, so grab value & remove from screen
[self.view.subviews[foundIndex] removeFromSuperview];
} else {
// not in hierarchy, so put it up on screen
[self.view addSubview:self.Picker];
[self.view bringSubviewToFront:self.Picker];
self.view.userInteractionEnabled = YES;
}
}

Related

Why does my textField disappear?

I do some practice with iPhone4S about UITextField on iOS 7.1.2.
Interface:
The interface of the first version is simple, which just has a custom UITextField named MyUITextField and a UIButtom object that use to cancel the search operation. Inside the textField, its leftView property is initially set to a UIButton object, which background image is a magnifying glass. When users tap in the textField, that magnifying glass button will be removed and the leftView property will also set to a new UIButton object, which background image is a inverted triangle. When users tap the triangle button, app will create a new view object.
Main code:
//In this version,the textField's original frame property is set to (10,40,250,30)
//and the main operations are like this:
- (void)viewDidLoad
{
[super viewDidLoad];
self.myTextField.delegate = self;
self.myTextField.leftViewMode = UITextFieldViewModeUnlessEditing;
self.myTextField.leftView = [self creCustomSearchBar];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(keyBoardWillAppear:)
name:#"UIKeyboardWillShowNotification"
object:nil];
[self.cancleSearchButton addTarget:self
action:#selector(cancleSearch:)
forControlEvents:UIControlEventTouchUpInside];
}
/*when tapping in the textField,keyboard will appear*/
- (void)keyBoardWillAppear:(NSNotification *)notification
{
CGRect newLeftViewIndicatorRect = CGRectMake(20,5,20,20);
UIButton *newLeftViewIndicator = [[UIButton alloc] initWithFrame:newLeftViewIndicatorRect];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:#"searchbar_textfield_down_icon#2x" ofType:#"png"];
[newLeftViewIndicator setBackgroundImage:[UIImage imageWithContentsOfFile:imagePath] forState:UIControlStateNormal];
[newLeftViewIndicator addTarget:self action:#selector(createActuralLeftView:) forControlEvents:UIControlEventTouchUpInside];
self.myTextField.leftView = newLeftViewIndicator;
/*in the second verson need to call */
//[self adjustViewFrame];
[self.cancleSearchButton setTitle:#"取消" forState:UIControlStateNormal];
}
- (void)createActuralLeftView:(UIButton *)leftViewButton
{
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapRegionAboveKeyboard:)];
[self.view addGestureRecognizer:tapGesture];
NewLeftView *leftView = [[NewLeftView alloc] initWithFrame:CGRectMake(10, 80, 140, 100)];
leftView.delegate = self;
[self.view addSubview:leftView];
}
- (void)tapRegionAboveKeyboard:(UITapGestureRecognizer *)tapGesture
{
for (UIView *v in self.view.subviews) {
if ([v isKindOfClass:[NewLeftView class]]) {
[v removeFromSuperview];
[self.view removeGestureRecognizer:tapGesture];
return;
}
}
}
//in the second version need to call
- (void)adjustViewFrame
{
[[self.myTextField class] animateWithDuration:0.05 animations:^{
[self.myTextField setFrame:CGRectMake(self.myTextField.frame.origin.x, 40, self.myTextField.frame.size.width, self.myTextField.frame.size.height)];
}];
[[self.cancleSearchButton class] animateWithDuration:0.05 animations:^{
[self.cancleSearchButton setFrame:CGRectMake(self.cancleSearchButton.frame.origin.x, 40, self.cancleSearchButton.frame.size.width, self.cancleSearchButton.bounds.size.height)];
}];
}
In the first version things work well.
But in the second version, I set the textField frame property to (10,260,250,30), so I need to call the adjustViewFrame method in the keyBoardWillAppear: to reposition the textField in case of obscuring by the keyboard.
Here comes the problem: the textField's position is correctly moved to the right place, but when I tap the inverted triangle button, the textField disappeared.
I can't figure out what's going wrong here.

Does it make sense to add ATMHud to tabBarController.view

When i try to add ATMHud to uitableviewcontroller subview it does work but it doesn't disable the scrolling and if the tableview is not on top i can't see the hud view. What i did was added to teh tabBarController.view that works but i want find out if this is a good idea or later on i might have issues with it.
Another question is tabBarController.view frame is that the whole screen or just the bottom part. How come atmhud shows in the middle of the screen?
Thanks in advance!
Yan
============
Found a blog post that shows how to reset self.view and add tableview separately in uitableviewcontroller
UITableViewController and fixed sub views
- (void)viewDidLoad {
[super viewDidLoad];
if (!tableView &&
[self.view isKindOfClass:[UITableView class]]) {
tableView = (UITableView *)self.view;
}
self.view = [[[UIView alloc] initWithFrame:
[UIScreen mainScreen].applicationFrame] autorelease];
self.tableView.frame = self.view.bounds;
self.tableView.contentInset = UIEdgeInsetsMake(44.0, 0.0, 0.0, 0.0);
[self.view addSubview:self.tableView];
UIView *fixedBar = [[UIView alloc] initWithFrame:
CGRectMake(0.0, 0.0, self.view.bounds.size.width, 44.0)];
fixedBar.backgroundColor = [UIColor colorWithRed:
0.0 green:1.0 blue:0.0 alpha:0.7];
[self.view addSubview:fixedBar];
[fixedBar release];
}
After this when add hud to self.view you will be able to disable the tableview on the bottom.
Let me know if this a good way to setup the tableview
The problem with using the tab bar is that the hud is now modal, and the user cannot change the tab.
It sounds like the tableview is not your primary viewm, as it can get "covered up". If its not the primary view, then add the ATMHud to self.view. If the tableView is the same as self.view, then add a new transparent view to it, then add the HUD to that view.
The tabBarController.view is the view that hosts the tabbed views - if you want to see its size (or frame) log it using NSStringFromCGRect(self.tabBarController.frame);
EDIT: I just did a test, the ATMHud DOES block the UI. All I can think of is that you have not inserted it where you need to (at the top of current view's subviews.) I have a demo project where I do this:
hud = [[ATMHud alloc] initWithDelegate:self];
[self.view addSubview:hud.view];
[hud setCaption:#"Howdie"];
[hud setActivity:YES];
[hud show];
[hud hideAfter:5];
A button under the hud is not active - in fact nothing in the view is active (probably the Nav Bar would be live though)
If you want an ARCified and field tested version, you can grab it here
EDIT2: The solution to your problem is below. Note that ATMHud blocks clicks from getting to the table, and the code below stops the scrolling:
- (void)hudWillAppear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = NO;
}
- (void)hudDidDisappear:(ATMHud *)_hud
{
self.tableView.scrollEnabled = YES;
}
Dump the views:
#import <QuartzCore/QuartzCore.h>
#import "UIView+Utilities.h"
#interface UIView (Utilities_Private)
+ (void)appendView:(UIView *)v toStr:(NSMutableString *)str;
#end
#implementation UIView (Utilities_Private)
+ (void)appendView:(UIView *)a toStr:(NSMutableString *)str
{
[str appendFormat:#" %#: frame=%# bounds=%# layerFrame=%# tag=%d userInteraction=%d alpha=%f hidden=%d\n",
NSStringFromClass([a class]),
NSStringFromCGRect(a.frame),
NSStringFromCGRect(a.bounds),
NSStringFromCGRect(a.layer.frame),
a.tag,
a.userInteractionEnabled,
a.alpha,
a.isHidden
];
}
#end
#implementation UIView (Utilities)
+ (void)dumpSuperviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
while(v) {
[self appendView:v toStr:str];
v = v.superview;
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
+ (void)dumpSubviews:(UIView *)v msg:(NSString *)msg
{
NSMutableString *str = [NSMutableString stringWithCapacity:256];
if(v) [self appendView:v toStr:str];
for(UIView *a in v.subviews) {
[self appendView:a toStr:str];
}
[str appendString:#"\n"];
NSLog(#"%#:\n%#", msg, str);
}
#end

UIPageControl doesn't properly react to taps

In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.

UIView in front of everything not rotating with screen?

I have this code:
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.image = [UIImage imageNamed:#"image.png"];
UIApplication *app = [UIApplication sharedApplication];
[app addSubViewOnFrontWindow:imageView];
...
- (void)addSubViewOnFrontWindow:(UIView *)view {
int count = [self.windows count];
UIWindow *w = [self.windows objectAtIndex:count - 1];
[w addSubview:view];
}
The problem is, when the app rotates, the view on the front doesn't rotate. It just stays in portrait.
How can I get this to rotate normally with the rest of the device?
I haven't tried this, but UIWindow has a property rootViewController
The root view controller for the window.
#property(nonatomic, retain) UIViewController *rootViewController
Discussion
The root view controller provides the content view of the
window. Assigning a view controller to this property (either
programmatically or using Interface Builder) installs the view
controller’s view as the content view of the window. If the window has
an existing view hierarchy, the old views are removed before the new
ones are installed.
The default value of this property is nil.
Availability
Available in iOS 4.0 and later.
Declared In
UIWindow.h
As you should be providing this root view controller you should be able to add it to the rootViewContoller's view, and handle aout rotation correctly.
another solution could be, that you swap the windo with a custom one while presenting your view, with another view controller. this trick in action you can see in the implementation of TSAlertView.
- (void) show
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:[NSDate date]];
TSAlertViewController* avc = [[[TSAlertViewController alloc] init] autorelease];
avc.view.backgroundColor = [UIColor clearColor];
// $important - the window is released only when the user clicks an alert view button
TSAlertOverlayWindow* ow = [[TSAlertOverlayWindow alloc] initWithFrame: [UIScreen mainScreen].bounds];
ow.alpha = 0.0;
ow.backgroundColor = [UIColor clearColor];
ow.rootViewController = avc;
[ow makeKeyAndVisible];
// fade in the window
[UIView animateWithDuration: 0.2 animations: ^{
ow.alpha = 1;
}];
// add and pulse the alertview
// add the alertview
[avc.view addSubview: self];
[self sizeToFit];
self.center = CGPointMake( CGRectGetMidX( avc.view.bounds ), CGRectGetMidY( avc.view.bounds ) );;
self.frame = CGRectIntegral( self.frame );
[self pulse];
if ( self.style == TSAlertViewStyleInput )
{
[self layoutSubviews];
[self.inputTextField becomeFirstResponder];
}
}
with
#interface TSAlertOverlayWindow : UIWindow
{
}
#property (nonatomic,retain) UIWindow* oldKeyWindow;
#end
#implementation TSAlertOverlayWindow
#synthesize oldKeyWindow;
- (void) makeKeyAndVisible
{
self.oldKeyWindow = [[UIApplication sharedApplication] keyWindow];
self.windowLevel = UIWindowLevelAlert;
[super makeKeyAndVisible];
}
- (void) resignKeyWindow
{
[super resignKeyWindow];
[self.oldKeyWindow makeKeyWindow];
}
//
#end
add a new window, with new rootviewController, and subscribe it to
[[NSNotificationCenter defaultCenter] addObserver:(id) selector:(SEL) name:UIDeviceOrientationDidChangeNotification object:nil];
Windows don't autorotate. Ever. You should be adding this imageView to a UIViewController's view as the UIViewController has built in autorotation.
If you want to use a UIWindow to hold all your views, you will need to write your own transform code to handle rotations.

NSMutableArray of UIImages - view stays stuck with last image

I'm sure that I'm missing something terribly easy here...
I loop through the images just fine the first time, the indices reset and increment properly, but the UIView doesn't refresh and keeps the last image in the array showing. transitionFromView: automatically swaps the subviews, right? Do I need to implement the completion: option?
What am I missing? Here's the pertinent code:
- (id)initWithFrame:(CGRect)frame
{
NSInteger maxSlides = 12;
self = [super initWithFrame: frame];
if (self) {
//set up an array of images to be used for a flipbook
slides = [[NSMutableArray alloc] initWithCapacity: maxSlides];
for(NSInteger i=0; i<maxSlides; ++i) {
imageView = [[UIImageView alloc] initWithImage:
[UIImage imageNamed:[NSString stringWithFormat: #"muybridge%d.png", i]]];
[slides addObject: imageView];
}
index = 0; //default to first photo.
[self addSubview: [slides objectAtIndex: index]];
}
return self;
}
- (void) touchesEnded: (NSSet *) touches withEvent: (UIEvent *) event
{
//output indices for debugging. (yes, they increment properly)
NSLog(#"index = %d", index);
NSLog(#"currentIndex = %d", currentIndex);
NSLog(#"nextIndex = %d", nextIndex);
if (nextIndex <= 10) {
currentIndex = nextIndex;
} else {
currentIndex = 0;
}
nextIndex = currentIndex + 1;
//flipbook - tapping the image transitions to the next image in the array
[UIView transitionFromView: [slides objectAtIndex: currentIndex]
toView: [slides objectAtIndex: nextIndex]
duration: 0
options: UIViewAnimationOptionTransitionCurlUp
completion: NULL
];
}
What you're missing is this, from the docs on transitionFromView: toView:... :
fromView
The starting view for the transition. By default, this view is removed from its superview as part of the transition.
So it works, the first time through, but only one subview, the last one, remains after you've looked at them all. Look at the UIViewAnimationOptionShowHideTransitionViews option.
According to the Apple reference for UIView: transitionFromView: toView:... :
This method modifies the views in their view hierarchy only. It does not modify your application’s view controllers in any way. For example, if you use this method to change the root view displayed by a view controller, it is your responsibility to update the view controller appropriately to handle the change.
Maybe try calling the [yourView setNeedsDisplay] method right after.