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];
}
}
}
Related
I have a UIViewController with many subviews like UILabels, UIImages and a UIWebview. With a defined action by the user, the subviews of the UIViewController animate to different sizes and different locations inside of the UIViewController's view. Is it possible that this can be undone with a different defined action by the user? I want to make all the subviews revert back to their previous locations and sizes that they were before the animation was run. I thought of two possible solutions:
Get the properties of the subviews with the view.subviews() method before the animation is run, and then set the subviews after the animation to the properties in this array, or,
Call a method on the UIViewController to tell it to redraw all the subviews according to the properties set in the storyboard file.
Are these the right way of accomplishing what I would like to do? And if so, how would I go about doing this? (I don't know how to programmatically implement either of my ideas.)
Any help is greatly appreciated. Thanks.
Here is the solution.
#interface ViewController ()
#property (strong, nonatomic) NSMutableArray *frames;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//Saving initial frames of all subviews
self.frames = [NSMutableArray new];
NSArray *allViews = [self allViewsOfView:self.view];
for (UIView *view in allViews) {
CGRect frame = view.frame;
NSValue *frameValue = [NSValue valueWithCGRect:frame];
[self.frames addObject:frameValue];
}
}
- (NSMutableArray *)allViewsOfView:(UIView *)view
{
NSMutableArray *result = [NSMutableArray new];
[result addObject:view];
for (UIView *subView in view.subviews) {
[result addObjectsFromArray:[self allViewsOfView:subView]];
}
return result;
}
- (void)resetFrames
{
NSArray *allViews = [self allViewsOfView:self.view];
for (UIView *view in allViews) {
NSValue *frameValue = [self.frames objectAtIndex:[allViews indexOfObject:view]];
CGRect frame = [frameValue CGRectValue];
view.frame = frame;
}
}
#end
Call [self resetFrame]; whenever you want to revert view's frames back to their initial values.
You could cache all your subview's frame before changing it and running the animation, in this way you can even cache more than one action. A stack structure will be perfect for this, but there is no way to achieve this in interface builder, you have to reference outlets from IB to code to get their frame.
I have a UICollectionView that is used to simulate the new calendar in iOS 7. This collection view is inside a controller that has a selectedDate property. Whenever the selectedDate property is set the collection view should scroll to the date in the collection view.
The calendar controller's viewWillAppear also ensure the selected date is visible because this controller is cached and reused.
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.calendarView scrollToDate:[self selectedDate] animated:NO];
}
The problem is that the VERY first time the calendar controller is shown the scroll does not work. The contentOffset of the collection view is not updated.
My current workaround is to schedule the scroll to occur on the next run loop using
dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^(void)
{
// Scroll to the date.
});
It looks like when the UICollectionView is not in a window you cannot scroll. Scheduling the scroll to happen on the next run loop ensure that the view has been added to the window and can be properly scrolled.
Has anyone else experienced this issue and what their workarounds?
you can always force auto-layout to layout.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
self.collectionView.scrollToItemAtIndexPath......
}
If you are using auto layout, the issue may be that the constraints haven't set the frames yet. Try calling the scrollToDate: method in viewDidLayoutSubviews (without dispatch_after).
#interface CustomViewController ()
#property (nonatomic) BOOL isFirstTimeViewDidLayoutSubviews; // variable name could be re-factored
#property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
#end
#implementation CustomViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.isFirstTimeViewDidLayoutSubviews = YES;
}
- (void)viewDidLayoutSubviews
{
// only after layoutSubviews executes for subviews, do constraints and frames agree (WWDC 2012 video "Best Practices for Mastering Auto Layout")
if (self.isFirstTimeViewDidLayoutSubviews) {
// execute geometry-related code...
// good place to set scroll view's content offset, if its subviews are added dynamically (in code)
self.isFirstTimeViewDidLayoutSubviews = NO;
}
bilobatum's answer is correct!
I'm writing this because I don't have reputation to comment... :/
I tried bilobatum's answer in my project, and it worked perfectly!
My code:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
if (currentOffset.y != 999) {
[collectionView setContentOffset:currentOffset animated:NO];
}
}
currentOsset is a CGPoint initialized with x = 0 and y = 999 values (CGPoint currentOffset = {0,999};)
In the viewWillDisappear method I save the collectionView's contentOffset in the currentOffset.
This way if I navigate to the controller that has the collectionView and I navigated to there before, I will always have the last position.
The code that will work for you:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
[self.calendarView scrollToDate:[self selectedDate] animated:NO];
}
Thank you bilobatum for the answer!
Using -viewDidLayoutSubviews created an infinite loop that made the solution too complicated.
Instead I just added a small delay to let the constraints be created before the scrolling:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if ([self.scheduleDate isThisWeek]) [self.calendarLayout performSelector:#selector(scrollToCurrentTime) withObject:nil afterDelay:1];
}
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!
I would like to know if a cell is being dragged (via the grip on the right side when a UITableView is in editing mode). I would like to change the background while it is in that state.
There is no callback unfortunately, but since the default behaviour is to adjust the 'alpha' value of the UITableViewCell you can check for that in '- (void)layoutSubview' of your UITableViewCell subclass.
- (void)layoutSubviews
{
[super layoutSubviews];
if (self.alpha < 0.99) {
// we are most likely being dragged
} else {
// restore for not being dragged
}
}
Note that this is a bit of a hack. I noticed that this doesn't work when the table view's separatorStyle is set to 'UITableViewCellSeparatorStyleNone'.
I don't know if this is possible in the framework. But if it's not, you can try to override touchedMoved method in an UITableViewCell subclass?
You might then be able to also retrieve the state of your tableView (editing or not) in your UItableViewCell's custom subclass.
It is possible, yes!
In ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIScrollViewDelegate>
{
IBOutlet UITableView * myTable;
IBOutlet UIScrollView * myScroll;
}
In ViewController.m
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
myScroll = myTable;
myScroll.delegate = self;
}
#pragma Mark UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSLog(#"%f",scrollView.contentOffset.y);
}
That is it.
It's possible yes! But you have to keep track on the states yourself in the view controller. Define a member
BOOL tableDrag = NO;
In the method
scrollViewWillBeginDragging
you set tableDrag = YES; And in the method
scrollViewDidEndDragging
you set tableDraw = NO;
Now in the scrollViewDidScroll delegate method you can set the background.
If you have several scrollers then tag the scroll view so that you know which one is dragging.
Hope it helps!
I have a main view with 3 buttons. Clicking on any of the buttons adds a SubView.
The buttons have different titles and are all linked to IBAction "switchView"
The "switchView" code is below.
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
[self.view addSubview:myViewController.view];
}
The "secondView" loads up correctly and everything works well.
The problem is I want to be able to know which button was the Sender.
I don't want to create 3 subviews, one for each button. The code and XIB would be absolutely the same>
The only difference would be a variable that I would like to set up in the second view (viewDidLoad method) depending on who is the Sender (which button was clicked)
Is this possible? Or I would need to create 3 subViews - one for each button?
Your help is greatly appreciated!
You can identify different buttons with the tag property.
e.g. with your method:
-(IBAction)switchView:(id)sender {
UIButton *button = (UIButton*)sender;
if (button.tag == 1) {
//TODO: Code here...
} else if (button.tag == 2) {
//TODO: Code here...
} else {
//TODO: Code here...
}
}
The tag property can be set via the InterfaceBuilder.
Hope this helps.
I think you can solve in 2 ways:
Create a property like:
#property (nonatomic, strong) IBOutlet UIButton *button1, *button2, *button3;
in your viewcontroller and link the buttons to them as referencing outlet on the XIB.
Give a different tag to each button on your xib and ask for the tag of the sender with UIButton *b=(UIButton*)sender; b.tag; like Markus posted in detail.
Solving my problem it all came down to transferring data between the mainView and subView.
In my mainView.h I declared an NSString and its #property
...
NSString *btnPressed;
}
#property(nonatomic, retain) NSString *btnPressed;
...
then in my mainView.m inside the switchView method I did this:
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
btnPressed = [NSString stringWithFormat:#"%i", [sender tag]];
[myViewController setBtnPressed:self.btnPressed];
[self.view addSubview:myViewController.view];
}
This line in the code above actually takes care of transferring the data to the newly created subView:
[myViewController setBtnPressed:self.btnPressed];
Then in my secondView.h I declare exactly the same NSString *btnPressed and its #property (though this a completely different object than the one declared in main)
Then in my secondView.m I get the value of the button pressed I'm interested in.
- (void)viewDidLoad {
[super viewDidLoad];
int theValueOfTheButtonPressed = [self.btnPressed intValue];
}
This works well.
Don't forget to #synthesize btnPressed; as well as [btnPressed release]; in both mainView.m and secondView.m