Default view (as UIScrollView) of UIViewController - Can't set its content offset - objective-c

According to my project, I changed the default view that tied to one UIViewController from UIView to UIScrollView as it has to be changed its offset and content size. The way I change from UIView to UIScrollView was by Interface Builder > Custom Class
If I want to change its offset and content size programmatically within The Controller, It can't be done in usual way unless I cast it
[((UIScrollView *)self.view) setContentSize:CGSizeMake(1024, 2000)];
[((UIScrollView *)self.view) setContentOffset:CGPointMake(0, 150)];
But with other UIScrollView within the app, changing their offset and content size can be done like any other,
// Assume that sv is scroll view object
[sv setContentSize:CGSizeMake(1027, 2000)];
[sv setContentOffset:CGPointMake(0, 150)];
When I print that scroll view object, it shown as a UIScrollView
NSLog(#"%#", self.view);
NSLog(#"%#", [self.view class]);
-> <UIScrollView: 0x6d3d3f0; frame = (0 0; 1024 768); autoresize = RM+BM; layer = <CALayer: 0x6d52ce0>; contentOffset: {0, 0}>
-> UIScrollView
Anyone know what I did missing or forgot to do rather than change the view's custom class?
Best and Thank you,
Tar
P.S. Besides the extra code needed, everything works fine. But, I can't take the chance of possible bug in the future. This client is super crazy, no error will be accepted

An alternative to this (that would avoid the need for casting) would be to set the UIViewController custom class back to UIView in interface builder. Then, insert a new UIScrollView as a subview (simply drag and drop in Interface Builder), connecting the UIScrollView to the UIViewController subclass via an IBOutlet that looks something like:
#property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
(When you connect the outlet that property declaration should be generated for you by XCode).
Then, in your UIViewController subclass, you can do something like:
[[self scrollView] setContentSize:CGSizeMake(1024, 2000)];
[[self scrollView] setContentOffset:CGPointMake(0, 150)];

I'm not sure what the best solution is but a quick workaround is to create a method that casts the view in the UIViewController. That is:
- (UIScrollView *)scrollView {
return (UIScrollView *)self.view;
}
Then, instead of referring to the view in the ViewController as self.view, refer to it as self.scrollView.

Related

inheritance in a UIView file

Simple question about inheritance
I have a standard program with a master view:
MasterViewController.h
enter code here#interface MasterViewController : UIViewController
using a subview for drawing:
MasterViewController.m
frame = CGRectMake(xo, yo, side*width, side*height); // maxSide
backView = [[BackView alloc] initWithFrame:frame];
[backView setBackgroundColor:[UIColor whiteColor]];
[self infoToBackView];
[self.view addSubview:backView];
BackView.h
#interface BackView : UIView
and BackView.m has its drawRect:
- (void)drawRect {
:
:
}
The problem I have is that I want BackView to inherit from MasterViewController, i.e. I want
Backview.h to be
#interface BackView : MasterViewController
which allows it to inherit the variables it needs from MasterViewController.
The problem is that this does not work; BackView must inherit from UIView to be able to draw with DrawRect. Therefore, before calling the UIView BackView, I must send it the variables it needs for drawing:
[self infoToBackView];
[backView setNeedsDisplay];
where infoToBackView is a method sending the needed variables to BackView.
The $64,000 question: How can I have a BackView that inherits from MasterViewController AND has drawRect?
BackView is a View, it is design to DRAW something.
ViewController are design to manage a view Herarchy.
They are not the same things. One is a plane, the other is an aiport! You cannot fly an airport...
UIView's subclass are design to draw. If you want to draw (text, shapes..) on screen, create a subclass of UIView. If you want to manage a scene in your app storyboard, create a subclass of UIViewController.
drawRect: is a method of UIView
Now you can pass variable to your view from your viewController, like a label (another kind of View) received a text to know what to display.
For example in your ViewController, you can have:
self.myBackView.color = [UIColor blueColor];
self.myBackView.progress = .5;
self.myBackView.text = #"MVC is awesome";
For a better understanding of the MVC design pattern, please reffer to Apple doc : https://developer.apple.com/library/mac/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html

UIImageView shifts left before animating to the right

Newbie here,
With this code in the .m file I aimed to animate an UIImageView to the right
#interface ViewController ()
#property (strong, nonatomic) IBOutlet UIImageView *myImageView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[UIView animateWithDuration:1
delay:0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
self.myImageView.center = CGPointMake(self.myImageView.center.x + 100, self.myImageView.center.y);
}
completion:^(BOOL finished){
}];
}
But what actually happens is that the image view shifts 100 points to the left and then animates to the right by 100 points back to its original position. I don't know why this happens. How can I achieve an animation that moves directly to the right?
Note: I'm not sure if this has to do with anything, but I added constraints in the interface builder to center this UIImageView in the middle.
Create IBOutlets for the constraints you want change. Just like you would for any other UIKit element. Then in your code instead of doing:
self.myImageView.center = CGPointMake(self.myImageView.center.x + 100, self.myImageView.center.y);
Do something like this:
self.imageViewTralingSpaceConstraint.constant = 100; //Or whatever value makes sense
Then in your animateWithDuration block add [self.view layoutSubViews];
you probably want to put this code in your viewDidAppear method rather than your viewDidLoad. this would make the animation happen after the view appears rather than before it. the viewDidLoad method is called after the view hierarchy is loaded into memory but not neccessarily after it is all laid out.
here is a link to the uiviewcontroller reference. https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/
check out these two methods there. if you continue doing iOS development you will get to know these methods really well.

UIView inside a UIViewController or better way to do it?

I have a problem on how to properly do a certain kind of action.
The image below shows a UIViewController, but the second part of the view is a custom UIView (the one with the profile pic, name and Show View button).
The subclassed UIView is allocated using this code:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
[self.view addSubview:profileView];
The problem is of course, that the button on the UIView can't show any view, since it's only the UIViewController that can push another ViewController to the window(correct?).
The UIView is used in several places in the app and needs to be added easily and have the same behavior across the application.
This worked great until I added the button, and I'm starting to think I've made this wrong, and there has to be a better way to do it (maybe change the UIView to something else?).
I was thinking I should be able to call:
self.superview
And then somehow get the ViewController so I can push another ViewController into the view hierarchy, but nope.
Any suggestions and a tips on how to do this correctly?
UPDATE:
I have no idea on how to push another UIViewController from the button.
What should I do in this method when pressing the button in the UIView:
- (void) showViewButtonTouched:(UIButton*)sender {
GPProfileSocialFriendsViewController *friendsSettings = [[GPProfileSocialFriendsViewController alloc] init];
}
How do I push GPProfileSocialFriendsViewController?
Your - (void) showViewButtonTouched:(UIButton*)sender method should be in your controller and would probably be better named - (void) showView:(UIButton*)sender or - (void) showProfile:(UIButton*)sender so it clearly denotes what it does (not how you got there).
It's not the view's responsibility to manage transitions from a state to another. If you move your method to your controller, your problem is no more (you can easily access self.navigationController or push directly if you don't have an navigation controller like this:
[self presentViewController:vcThatNeedsToBePushed animated:YES completion:nil];
I think you can create weak reference in GPProfileView on UIViewController. Like this:
#property (weak, nonatomic) UIViewController *rootController;
when you create GPProfileView, assign rootController-property:
profileView = [[GPProfileView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 70)];
profileView.myTripGradientColor = YES;
profileView.rootController = self; // if view created in view controller
[self.view addSubview:profileView];
and in implementation of button selector:
self.rootController push... // or pop
May be this not correct, but you can try
You could let the view controller push the next view controller when the button is pushed. The view controller can add a target/action on the button, so that the action method in the view controller is called on the touch up inside event.

How to access subviews in nested UIScrollViews

I was having trouble getting a UIScrollView with multiple UIImageView subviews to zoom properly so I tried using a UIScrollView with multiple UIScrollView subviews inside of it and a UIImageView inside of each of those. That way one UIScrollView can only scroll and the other can only zoom.
My question is: How can I access subviews of a subview. I know that normally I can access subviews using [scrollView viewWithTag:tagInt]; for example, but I can't seem to access the subviews of a subview using [[scrollView viewWithTag:tagInt] viewWithTag:tagInt2]; since viewWithTag only returns a single UIView and not all of it's subviews.
I could always give each subview a unique tag and access them that way, but that doesn't seem to be the most elegant solution.
What is the best way to access a subView and then get to the subView's subview (ie: access my UIScrollView used for zooming which is a subview of my main view, and then access it's subView UIImageView)?
If you don't feel comfortable subclassing for now, you'd have to assign tags to the UIScrollViews containing images. You'd then have to do what the BobC suggested with a little more work.
for (UIView *subview in [myScrollView subviews])
{
//check if the current subview is one of the UIScrollViews
if (subview.tag > 100)
//do something with the UIScrollView
}
The most elegant way to do this would be to subclass UIScrollView, give it a UIImageView property. That way, you could access this UIImageView with something like:
MyScrollView *myScrollView = (MyScrollView *)[scrollView viewWithTag:tagInt];
UIImageView *imageView = myScrollView.imageView;
Hope this helps!
You can get an array of subviews of any UIView using subviews property.
for(UIView *subview in [myScrollView subviews]) {
// do anything with your subview here.
}

subview running but not showing

From withing a UIViewController that is tied to a UIView (drawn in a nib file), i try to add another view, as a subview to the first view.
In case you are confused: UIViewController -> UIView + GraphView (extends UIView)
So i am saying:
GraphView *myGraphView = [[GraphView alloc] init];
graphView = myGraphView;
[self.view addSubview:graphView];
[myGraphView release];
I have also tried with insertSubview.
The UIView shows up and the GraphView subview is instantiated correctly (its properties are there and i can access its methods). But it never shows on the screen!
Its drawRect method is never called (i have an NSLog in there that never shows), even if i manually call [graphView setNeedsDisplay].
Does anyone have a clue?
Thanks a lot!!!
Doesn't look like you're setting the GraphView frame rectangle, it will not display if it's offscreen. Also you probably should be calling the UIView initWithFrame: initializer if you aren't.