I have a UIScrollView in which I am adding a subview which is a TimeLineView(a UIView).For the purpose of displaying; initially I just did the same and the TimeLineView got displayed without any problem. But now I want to have a UIView just above the UIScrollView where I can display some information about the the timeline.
Do I need to decrease the height of the UIScrollView so that the UIView gets fit on the space above it? If yes, How would I do that?
If there's another way...then please let me know.
All my initial implementations are as follows :
in .h
#property (nonatomic, readonly) UIScrollView *scrollView;
#property (nonatomic, readonly) TimeLineView *timelineView;
in .m
#synthesize scrollView = scrollView;
#synthesize timelineView = timelineView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self prepareCustomView];
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super initWithCoder:decoder];
if (self) {
[self prepareCustomView];
}
return self;
}
- (void)prepareCustomView {
// Initialization code
// Add main scroll view
[self addSubview:self.scrollView];
// Add timeline view inside scrollview
[self.scrollView addSubview:self.timelineView];
}
- (UIScrollView *)scrollView
{
if (!scrollView) {
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, TOP_BAR_HEIGHT, self.bounds.size.width, self.bounds.size.height-TOP_BAR_HEIGHT-FOOTER_HEIGHT)];
scrollView.contentSize = CGSizeMake(self.bounds.size.width,TIMELINE_HEIGHT);
scrollView.scrollEnabled = TRUE;
scrollView.backgroundColor =[UIColor whiteColor];
scrollView.alwaysBounceVertical = TRUE;
}
return scrollView;
}
Thanks in advance for the help.
Yes you have to change y-axis and decrease the height of scrollview by the height of your UIView.You have to change the frame of your scrollview as follows if the scrollview is just below UIView:
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0f, UIViewFrame.size.height+UIViewFrame.origin.y, self.bounds.size.width, self.bounds.size.height-TOP_BAR_HEIGHT-FOOTER_HEIGHT-UIViewFrame.size.height)];
where UIViewFrame is the frame of your UIView.
Related
I have a custom UIView XIB that gets loaded onto my ViewController and the frame is adjusted to be collapsed before added to subview. The UIButton tied to my XIB is showing when the frame is smaller than the button location. How can I hide and show my UIButton as the frame is expanding/collapsing? My XIB is not using AutoLayout.
ViewController.m
self.greenView = [[[NSBundle mainBundle] loadNibNamed:#"Green" owner:self options:nil] objectAtIndex:0];
[self.navigationController.view addSubview:self.greenView];
GreenView.h
#interface GreenView : UIView
#property (weak, nonatomic) IBOutlet UIView *expandView;
#property (nonatomic, getter = isExpanded) BOOL expand;
#property (weak, nonatomic) IBOutlet UIButton *testButton;
- (IBAction)testButtonTapped:(id)sender;
#end
GreenView.m
#interface GreenView()
#property (nonatomic) UITapGestureRecognizer *tapGesture;
#end
#implementation GreenView
- (void)awakeFromNib {
CGRect frame = self.frame;
frame.size.height = self.expandView.frame.size.height;
frame.origin.y = 200;
self.frame = frame;
self.expand = NO;
[self.expandView addGestureRecognizer:self.tapGesture];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (UITapGestureRecognizer *)tapGesture {
if (!_tapGesture) {
_tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapClicked:)];
}
return _tapGesture;
}
- (void)tapClicked:(UIGestureRecognizer *)recognizer {
CGRect frame = self.frame;
self.expand = !self.expand;
frame.size.height = self.expand ? gvExpandedHeight : gvCollapsedHeight;
[UIView animateWithDuration:0.5 animations:^{
self.frame = frame;
} completion:^(BOOL finished) {
NSLog(#"Frame after animation: %#", NSStringFromCGRect(self.frame));
}];
}
- (IBAction)testButtonTapped:(id)sender {
NSLog(#"Test button tapped");
}
#end
Collapsed:
Expanded:
GreenView.xib:
GrenenView.xib Struts and Springs:
If I understood you just want to clip it, if the button is outside to the view bounds. In -awakeFromNib add:
self.clipsToBounds = YES;
Using this property you are telling the view to cut everything that is not inside its bounds, views caw draw subviews even if they are place outside. Is a little expensive by means of performance, if you use it a lot or during heavy animations.
One way around could be hide it while the view is collapsed.
You change the frame to frame.origin.y = 200;
I think you have to set the UIButton constraint relative to your GreenView in you nib file
I am a newbie to iphone development and i am doing a simple app for practice. In that app,there is only one view that has an image on it. I want to be able to swipe left or right and have the first image go out of the view and the second image to come in. The images are different and I want it to look like they are connected. I need it to be able to change the image or restart after a series of swipes. I am doing this using ScrollView only.
I have tried something:
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UIScrollViewDelegate>
{
UIImageView *imgView;
UIScrollView *scrollView;
}
#property (nonatomic,retain)IBOutlet UIImageView *imgView;
#property (nonatomic, retain) IBOutlet UIScrollView *scrollView;
//- (id)initWithPageNumber:(int)page;
- (IBAction)changePage:(id)sender;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize imgView,scrollView;
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
- (id)initWithPageNumber:(int)page{
if (self = [super initWithNibName:#"MyView" bundle:nil])
{
pageNumber = page;
}
return self;
}
*/
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollview
{
scrollView.contentOffset = CGPointMake(scrollview.bounds.size.width, 0);
}
- (IBAction)changePage:(id)sender
{
NSArray *images = [[NSArray alloc] initWithObjects:#"1.jpeg",#"2.jpeg",#"3.jpeg", nil];
scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:scrollView];
CGRect cRect = scrollView.bounds;
for (int i = 0; i < images.count; i++){
imgView = [images objectAtIndex:i];
imgView.frame = cRect;
[scrollView addSubview:imgView];
cRect.origin.x += cRect.size.width;
}
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * images.count, scrollView.bounds.size.height);
scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
}
#end
When i run the app, I am still not able to swipe across the images. Am i doing something wrong? Need some guidance on this. Thanks..
This error indicates that you have a connection in Interface Builder with an IBOutlet that does not exist in your view controller class.
Make sure your IBOutlets are named correctly and that you have removed all obsolete connections in IB.
scrollView.ContentSize = (scrollView.frame.size.width * images.count
,scrollView.frame.size.height)
do this after lopping it will swipe
I am trying to navigate from a UIScrollView's content view to another view by using a UIButton. I do not want to display this other view modally, and pushing the view on the stack is not working for me.
Example:
My Viewcontroller contains a Scrollview that has 2 content views. Within these content views I would like to push another view onto the stack.
Is what I am trying to do possible?
Ok let's see if I understood well enough your problem.
#interface MyViewController ()
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) UIView *firstPlayerView;
#property (nonatomic, strong) UIView *secondPlayerView;
#end
#implementation MyViewController
-(UIView *)firstPlayerView
{
if (!_firstPlayerView) {
_firstPlayerView = [[UIView alloc] initWithFrame:self.view.bounds];
// set up your view as you like and place the button to go to the second player
// view when you need to.
// let's suppose that you called that button "goToSecondPlayerViewButton"
[goToSecondPlayerViewButton addTarget:self
action:#selector(switchPlayer)
forControlEvents:UIControlEventTouchUpInside];
}
return _firstPlayerView;
}
-(UIView *)secondPlayerView
{
if (!_secondPlayerView) {
_secondPlayerView = [[UIView alloc] initWithFrame:CGRectOffset(self.view.bounds, self.view.bounds.size.width, 0)];
// set up your view as you like and place the button to go to the first player
// view when you need to.
// let's suppose that you called that button "goToFirstPlayerViewButton"
[goToFirstPlayerViewButton addTarget:self
action:#selector(switchPlayer)
forControlEvents:UIControlEventTouchUpInside];
}
return _secondPlayerView;
}
-(UIScrollView *)scrollView
{
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
_scrollView.autoresizingMask = UIViewAutoresizingNone;
[_scrollView addSubview:self.firstPlayerView];
[_scrollView addSubview:self.secondPlayerView];
_scrollView.contentSize = CGSizeMake(self.view.frame.size.width * 2, self.view.frame.size.height);
_scrollView.pagingEnabled = YES;
_scrollView.scrollEnabled = NO;
_scrollView.bounces = NO;
_scrollView.showsVerticalScrollIndicator = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
}
return _scrollView;
}
-(void)switchPlayer
{
if(self.scrollView.contentOffset.x == 0) {
[self.scrollView scrollRectToVisible:CGRectMake(self.scrollView.bounds.size.width, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height) animated:YES];
self.title = #"Second Player";
} else {
[self.scrollView scrollRectToVisible:self.scrollView.bounds animated:YES];
self.title = #"First Player";
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self.view addSubview:self.scrollView];
self.title = #"First Player";
}
#end
I hope that solves your problem! I haven't tested the code so if you encounter problems just comment and I'll try to help.
Edited : To add a view you just enlarge the scroll view contentSize and add that view as its subview
-(UIView *)thirdView
{
if (!_thirdView) {
_thirdView = [[UIView alloc] initWithFrame:CGRectOffset(self.view.bounds * 2, self.view.bounds.size.width, 0)];
// set up your view as you like
}
return _thirdView;
}
-(void)addThirdView
{
self.scrollView.contentSize = CGSizeMake(self.scrollView.bounds.size.width * 3, self.scrollView.bounds.size.height);
[self.scrollView addSubview:self.thirdView];
[self.scrollView scrollRectToVisible:CGRectMake(self.scrollView.bounds.size.width * 2, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height) animated:YES];
}
You can generalize this and set your content size first then have a single method that takes an index as an argument.
-(void)scrollToViewAtIndex:(NSInteger)index
{
[self.scrollView scrollRectToVisible:CGRectMake(self.scrollView.bounds.size.width * index, 0, self.scrollView.bounds.size.width, self.scrollView.bounds.size.height)
animated:YES];
}
After seeing some examples, I'm trying to create a window where an Image view could be moved with the PanGestureRecognizer. I don't understand what is missing.
I created and initialized an UIView object
I also created an UIGestureRecognizer for panning, for both views.
I created the method to be selected when Gesture is recognized.
If you have an idea of what is missing, I would be thankful to read it;
Here is my code:
View Controller.h
#import <UIKit/UIKit.h>
#import "bouton.h"
#interface ATSViewController : UIViewController {
boutonHome *bouton; }
#property (retain, nonatomic) boutonHome *bouton;
-(IBAction)handlePanGesture:(UIPanGestureRecognizer*)sender;
#end
View Controller.m
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *image = [UIImage imageNamed:#"back.png"];
CGRect frame = CGRectMake(0, 0, image.size.width, image.size.height);
// Set self's frame to encompass the image
bouton.frame = frame;
bouton.boutonImage = image;
[self.view addSubview:bouton];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
[bouton setUserInteractionEnabled:YES];
[bouton addGestureRecognizer:panGesture];
[self.view setUserInteractionEnabled:YES];
[self.view addGestureRecognizer:panGesture];
// Do any additional setup after loading the view, typically from a nib.
}
-(IBAction)handlePanGesture:(UIPanGestureRecognizer *)sender
{
CGPoint translate = [sender translationInView:self.view];
CGRect newFrame = bouton.frame;
newFrame.origin.x += translate.x;
newFrame.origin.y += translate.y;
sender.view.frame = newFrame;
if(sender.state == UIGestureRecognizerStateEnded)
bouton.frame = newFrame;
}
BoutonHome.h
#import <Foundation/Foundation.h>
#interface boutonHome : UIView
{
UIImage *boutonImage;
}
#property (nonatomic, retain) UIImage *boutonImage;
// Initializer for this object
#end
boutonHome.m
#import "bouton.h"
#implementation boutonHome
#synthesize boutonImage;
#end
I'm developing on iOS and I'm building my views programmatically. I noticed that when I try to access variables that have to be changed in my view from the view controller they are null. I'll post both the view and its view controller:
RootViewController.h
#import <UIKit/UIKit.h>
#class RootView;
#interface RootViewController : UIViewController {
RootView *rootView;
}
#property (nonatomic,retain) RootView *rootView;
#end
RootViewController.m
#import "RootViewController.h"
#import "RootView.h"
#implementation RootViewController
#synthesize rootView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[rootView release];
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)loadView{
RootView *rootV = [[RootView alloc] initWithFrame:CGRectMake(10, 10, 100, 50)];
rootV.rootViewController = self;
self.view = rootV;
[rootV release];
}
- (void)viewDidLoad{
NSLog(#"TEXT: %#",self.rootView.label.text);
self.rootView.label.text=#"HELLO!";
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
[self setRootView:nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
RootView.h
#import <UIKit/UIKit.h>
#class RootViewController;
#interface RootView : UIView {
RootViewController *rootViewController;
UILabel *label;
}
#property (nonatomic,assign) RootViewController *rootViewController;
#property (nonatomic,retain) UILabel *label;
#end
RootView.m
#import "RootView.h"
#import "RootViewController.h"
#implementation RootView
#synthesize rootViewController;
#synthesize label;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//Create the label
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100,100, 50)];
//Set the font bold
testLabel.font = [UIFont boldSystemFontOfSize:20.0];
//Set the backgroundcolor of the label to transparent
testLabel.backgroundColor = [UIColor clearColor];
//Set the text alignment of the text label to left
testLabel.textAlignment = UITextAlignmentLeft;
//Set the text color of the text label to black
testLabel.textColor = [UIColor blackColor];
testLabel.text = #"01:30";
self.label = testLabel;
[self addSubview:label];
[testLabel release];
}
return self;
}
- (void)dealloc
{
[label release];
rootViewController = nil;
[super dealloc];
}
#end
I changed the code but it seems not working.....
Ok solved I forgot this line "self.rootView = rootV;"
Your view doesn't find out what its controller is until after its -initRootView method returns, but you're trying to use the controller from within that method.
That said, it would be much better if you followed the usual Cocoa Touch pattern for a view controller creating its view. View controllers are supposed to create their views lazily, which is to say that they defer view creation and initialization until the -loadView method is called. You can override -loadView to create your view, and also override -viewDidLoad to do any setup work that needs to be done after the view is created.
Also, it's generally not advisable for a view to know about its controller. The controller should tell the view what to do, not the other way around. If you need the view to send some information to the controller, you usually provide the controller to the view as the view's delegate. But if you just need the view controller to be able to find some subview, like your label, it's probably a good idea to either provide some accessors in the container view for that (so that the view controller can just say something like self.view.label.text = #"some text";. Another options is to set the subview's tag property to some unique value and have the controller use that to find the subview.
The problem is easy to spot, but requires some work to fix.
Looking at your code, something that I immediately want to suggest is to put all your RootView initialization code the loadView method of your RootViewController. That's where it should be (see here why).
Also, if you absolutely need your RootView to have a reference back at RootViewController, you should probably do that in viewDidLoad. But I wouldn't recommend doing that.
When using the MVC pattern, it is the controller's responsibility to initialize and update views. The line self.rootViewController.rootViewLabel = testLabel; should be removed from RootView's implementation. It's not clear what your intention is there, but if you want the rootViewLabel updated, you should let the controller do that.
To sum it all up:
// RootViewController.m
- (id)initRootViewController{
self = [super init];
if(self){
// other init code here
}
return self;
}
- (void)loadView {
RootView *rootV = [[RootView alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
self.view = rootV;
[rootV release];
}
- (void)viewDidLoad {
[super viewDidLoad];
// etc...
}
// etc.
Now, as for RootView, here is what it would look like:
RootView.h
#import <UIKit/UIKit.h>
#interface RootView : UIView {
UILabel *rootViewLabel;
}
// moved from RootViewController
#property (nonatomic, retain) UILabel *rootViewLabel;
#end
RootView.m
#import "RootView.h"
#implementation RootView
#synthesize rootViewLabel;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Whatever initialization code you might have
//Create the label
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 100,100, 50)];
//Set the font bold
testLabel.font = [UIFont boldSystemFontOfSize:20.0];
//Set the backgroundcolor of the label to transparent
testLabel.backgroundColor = [UIColor clearColor];
//Set the text alignment of the text label to left
testLabel.textAlignment = UITextAlignmentLeft;
//Set the text color of the text label to black
testLabel.textColor = [UIColor blackColor];
testLabel.text = #"01:30";
self.rootViewLabel = testLabel;
[testLabel release];
// add rootViewLabel as a subview of your this view
[self addSubView:rootViewLabel];
}
return self;
}
- (void)dealloc
{
[rootViewLabel release];
[super dealloc];
}
#end
I hope this gives you an idea on how to structure your view initialization code...
(Disclaimer, I can't test this code now, please point out any errors! Thanks)
Not sure why you're doing it like that, but you could probably make it work if you pass the controller into the view's init:
RootView *rootV = [[RootView alloc] initRootView:self];
view's init:
- (id)initRootView:(UIViewController*)controller
{
self.rootViewController = controller;
self.rootViewController.rootViewLabel = testLabel;