iOS - Resizing UIScrollView content at runtime - ios

I have a view that looks like the following:
The red rectangle (referencing outlet: self.redView) is put inside of a scrollview (referencing outlet: self.scrollView). When the "Bigger" or "Smaller" buttons are pressed, the red rectangle expands / contracts its height.
My code is here:
#import "s1cViewController.h"
#interface s1cViewController ()
#end
#implementation s1cViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidLayoutSubviews {
[self.scrollView setScrollEnabled:YES];
[self.scrollView setContentSize:CGSizeMake(self.scrollView.frame.size.width, 600)];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)btnBiggerTouched:(id)sender {
[self resizeViewHeight:20];
}
- (IBAction)btnSmallerTouched:(id)sender {
[self resizeViewHeight:-20];
}
- (void)resizeViewHeight:(int)heightDelta {
[UIView animateWithDuration:0.2f animations:^{
// Resize scrollview height
/*self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, self.scrollView.contentSize.height + deltaInputWrapperHeight);*/
// Resize categoryInputView height
CGRect rect = self.redView.frame;
rect.size.height += heightDelta;
self.redView.frame = rect;
}];
}
#end
When I change the height of the rectangle, and then scroll, the height changes back to its original size. How do I prevent this?

Perhaps you are using auto layout. If so, that's the problem. You cannot change a view's frame directly under auto layout, because if you do, when layout comes along, it will just change it right back (as seems to be happening here). You have to change the constraints.

Related

Implementing Inneractive / Inneractive banner ads - ad positioning issue

I am trying to use the Inneractive Ad SDK 5+ in my application and I'd like to position the ad on the bottom. In every case I've tried it will always be in the middle of the screen, centred horizontally and vertically.
Code:
self.myAdView = [[IaAdView alloc]
initWithAppId:#"MyAppID_iPhone" adType:IaAdType_Banner delegate:self];
self.myAdView.adConfig.refreshIntervalInSec = 30;
[[InneractiveAdSDK sharedInstance] loadAd:self.myAdView];
Delegate method:
- (void)InneractiveAdLoaded:(IaAd *)adView {
NSLog(#"AD LOADED METHOD CALLED");
// Here I tried many things I found online without any effect
[self.view addSubview:self.myAdView];
}
Here how i implement the property at the top of the viewcontroller.m file:
#interface ViewController () <InneractiveAdDelegate>
#property (nonatomic, retain) IaAdView* myAdView;
#end
#implementation ViewController
#synthesize myAdView = _myAdView;
I am sure it must be just a tiny thing but I can't figure it out. I found many solutions that include initWithFrame but this is not usable in this case.
Any help would be appreciated.
Update 1:
I've tried:
- (void)InneractiveAdLoaded:(IaAd *)adView {
NSLog(#"AD LOADED METHOD CALLED");
self.myAdView.frame=CGRectMake(x,y,width,height); // With any number and random number
and
self.myAdView.frame=CGRectOffset(self.myAdView.frame, 0, yOffset); // With any number and random number
[self.view addSubview:self.myAdView];
}
Nothing helped. Ad stays absolutely centred. I am starting to think there is something wrong with the setup but besides the positioning everything is working fine. See photo below.
Update 2:
Tried the following without success. ios8 shows banner centred on screen and ios7 for some reason not at all once the line has been added.
- (void)InneractiveAdLoaded:(IaAd *)adView {
NSLog(#"AD LOADED METHOD CALLED");
self.myAdView.frame = CGRectMake(0, self.view.frame.size.height - self.myAdView.frame.size.height , self.view.frame.size.width, self.myAdView.frame.size.height);
[self.view addSubview:self.myAdView];
}
Here's a working solution:
You have two options here -
Wrapping your adView in the external UIView and then you have the full control
Updating the frame of adView on the 'InneractiveAdLoaded:' event
ANYWAY: adView must be added to it's superView before calling 'loadAd:' SDK method.
Let me know, if you have any questions.
Update:
Option 2:
#import "ViewController.h"
#import "InneractiveAdSDK.h"
#interface ViewController () <InneractiveAdDelegate>
#property (nonatomic, strong) IaAdView *adView;
#end
#implementation ViewController {}
- (void)viewDidLoad {
[super viewDidLoad];
self.adView = [[IaAdView alloc] initWithAppId:#"<Your ad unit ID>" adType:IaAdType_Banner delegate:self];
[self.view addSubview:self.adView];
[[InneractiveAdSDK sharedInstance] loadAd:self.adView];
}
- (void)positionAd {
const CGFloat x = (self.view.bounds.size.width - self.adView.frame.size.width) / 2.0f;
const CGFloat y = self.view.bounds.size.height - self.adView.frame.size.height;
self.adView.frame = CGRectMake(x, y, self.adView.frame.size.width, self.adView.frame.size.height);
}
#pragma mark - InneractiveAdDelegate
- (void)InneractiveAdLoaded:(IaAd *)adView {
[self positionAd];
}
- (void)InneractiveDefaultAdLoaded:(IaAd *)adView {
[self positionAd];
}
- (UIViewController *)viewControllerForPresentingModalView { return self; }
#end
Try setting its frame this way:
self.myAdView.frame=CGRectMake(x,y,width,height);
Where x,y,width,height is your desired position and size.
Or if its already positioned nicely by X coordinate, width and height you could use:
self.myAdView.frame=CGRectOffset(self.myAdView.frame, 0, yOffset);
Where yOffset is how much you want it moved on that coordinate. Be sure you calculate it using self.view.frame and not with exact number, so its on bottom on every device.
To position your banner at the bottom of your UIView you need to change its frame.
self.myAdView.frame = CGRectMake(x, y, width, height);
self.myAdView.frame = CGRectMake(0, self.view.frame.size.height - self.myAdView.frame.size.height , self.view.frame.size.width, self.myAdView.frame.size.height);
x - Set to origin of 0
y - Set origin to the UIView's height. But this will make it appear off the bottom of the screen. So, we must subtract the height of our self.myAdView to move it up onto the screen: self.view.frame.size.height - self.myAdView.frame.size.height
width - Set it to the width of the UIView: self.view.frame.size.width
height - Set it to the height of our self.myAdView: self.myAdView.frame.size.height

Best method to resize UIView when rotated to landscape and back

Since I am very new to ios programming I have more of a general-design question.
I have a ViewController which contains a GraphView (UIScrollView + UIView) which works fine. When I am rotating to landscape I want the GraphView to resize its height to the display height (so it fills the whole screen) but only 300pts when in portrait.
What I did so far is implementing viewWillLayoutSubviews in the ViewController and resetting the constraints:
- (void)viewWillLayoutSubviews{
_graphViewHeightConstraint.constant = ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait) ? 300:[[UIScreen mainScreen] bounds].size.height-self.navigationController.navigationBar.frame.size.height - 2*_distanceToTopView.constant;
}
and in GraphView.m:
- (void)layoutSubviews{
kGraphHeight = self.frame.size.height;
[self setNeedsDisplay];
}
(because I need the variable kGraphHeight in the code to draw the Graph). This does not seem like a very elegant solution so I wanted to ask what the better way would be? Many thanks for your inputs :)
In GraphView.m
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
kViewWidth = <GET_SCREEN_WIDTH_HERE>;
kViewHeight = <GET_SCREEN_HEIGHT_HERE>;
[self updateViewDimensions];
}
and updateViewDimensions method will set the frame of UIScrollView and UIView
- (void)updateViewDimensions
{
scrollView.frame = self.view.frame;
yourView.frame = CGRectMake(kViewXStartsFrom, kViewYStartsFrom, kViewWidth, kViewHeight);
}
after rotating view to Landscape viewDidLayoutSubviews will be called.
It's working for me.

Testing autolayout with XCTest

I'm trying to figure out if there is a way to test the layout of an iOS view in unit tests when using autolayout. Right now I try to simply initialize the view controller, and then check the frames of the views. However, the frame on each view remains origin=(x=0, y=0) size=(width=0, height=0).
- (void)setUp {
[super setUp];
_viewController = [[AddPlayerViewController alloc] init];
_viewController.view.frame = CGRectMake(0, 0, 320, 460);
[_viewController view];
}
- (void)testViewsAreInsideWindow {
[self checkIfViewIsInsideWindow:_viewController.txtfNewPlayer];
[self checkIfViewIsInsideWindow:_viewController.btnNewPlayer];
[self checkIfViewIsInsideWindow:_viewController.tblPlayers];
}
- (void)checkIfViewIsInsideWindow:(UIView *)view {
CGRect windowFrame = _viewController.view.frame;
CGRect viewFrame = view.frame;
XCTAssertTrue(CGRectContainsRect(windowFrame, viewFrame));
}
I've tried adding
[_viewController.view needsUpdateConstraints];
or
[_viewController.view updateConstraints];
or
[_viewController updateViewConstraints];
or
[_viewController viewWillAppear:YES];
but none of them have helped.
Is it at all possible to get autolayout to run when using XCTest?
Have you tried setNeedsLayout followed by layoutIfNeeded?
You can definitely get layout to run in tests, I do it here, but that doesn't have a view controller, just views.
I believe what you are trying to see is whether elements are visible to the user. You may use isHittable property in XCUIElement.
From documentation https://developer.apple.com/documentation/xctest/xcuielement/1500561-ishittable
isHittable returns true if the element exists and can be clicked, tapped, or pressed at its current location. It returns false if the element does not exist, is offscreen, or is covered by another element.

Get X position of Frame in ScrollView

I have a CustomScrollView which holds a list of UILabels and scrolls through them (the subclassing is for automatic positioning of the views). In the class I have a list of these views stored as an NSMutableArray. I have found that when I ask for label.view.frame.origin after getting the label from the array, I always get {0, 0}. Because of this, the logic I use to scroll does not work (the scroll is done programmatic-ally). I am trying to have the UILabel scroll to the center of the frame of the CustomScrollView. Here is a code sample to show where I am having problems:
#interface CustomScrollView : UIScrollView
#property (nonatomic) NSMutableArray* labels; //This is the array of UILabels
#end
#implementation CustomScrollView
-(void)scrollToIndex:(int)index withAnimation:(bool)animated
{
UILabel *label = (UILabel*)self.labels[index];
CGRect rect = CGRectMake(label.frame.origin.x - ((self.frame.size.width - label.frame.size.width) / 2), 0, self.frame.size.width, self.frame.size.height);
[self scrollRectToVisible:rect animated:animated];
}
#end
TL:DR - label.frame.origin.x is returning me 0 and not what it's relative position is within the superview.
Bonus - Whenever I instantiate my custom scroll view, it automatically adds two subviews which I have no idea where the come from. I set the background color and turn off the scroll bars and scroll enabled.
Thanks in advance for the help.
Edit [Jun 25 11:38] - Turns out the rect I am creating to scroll is correct, but calling [self scrollRectToVisible:rect animated:animated] is just not working.
Edit [Jun 25 12:04] - Here is the method which I call every time I add a subview
-(UILabel*)lastLabel
{
if (self.labels.count == 0)
return nil;
return (UILabel*)self.labels.lastObject;
}
-(void)adjustContentSize
{
if (self.labels.count > 0)
{
float lastModifier = [self lastLabel].frame.origin.x + [self lastLabel].frame.size.width + ((self.frame.size.width - [self lastLabel].frame.size.width) / 2);
self.contentSize = CGSizeMake(MAX(lastModifier, self.contentSize.width), self.frame.size.height);
}
}
Try using the convertRect function:
CGRect positionOfLabelInScrollView = [self.scrollView convertRect:myLabel.frame toView:nil];
[self.scrollView setContentOffset:CGPointMake(0, topOfLabel - positionOfLabelInScrollView.origin.y + positionOfLabelInScrollView.size.height) animated:YES];
This should scroll your scrollView to display the label.

Can a UIView animation be used for animating a contained controller?

I have the following which works fine for somethlike a UIImageView. I am not sure if a UIView animation would work with a contained controller? I know I am animating the view's frame but just not sure if this would / should work (the below code is just proof-of-concept). But even if it did, the below is not working. Any ideas on why not?
thx in advance
edit #1
I've added a screen shot that shows a custom view controller in yellow with the phrase holla in it. As you can see the below code aniamtes the frame but not the elements of that view controller. Is this intended? Is there a workaround?
#import "MyViewController.h"
#interface ViewController ()
#property (nonatomic, strong) MyViewController *firstIV;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// lets add a few UIImageViews that we will animate
MyViewController *tmpFirstIV=[[MyViewController alloc] init];
tmpFirstIV.view.backgroundColor=[UIColor yellowColor];
tmpFirstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 100.0f);
self.firstIV=tmpFirstIV;
[self addChildViewController:self.firstIV];
[self.view addSubview:self.firstIV.view];
[self.firstIV didMoveToParentViewController:self.firstIV];
-(void)updateAction:(id) sender{
NSLog(#"Here are some results");
if([self.updateButton.currentTitle isEqualToString:#"update this"]){
[UIView animateWithDuration:6.0f animations:^{ // yeah I know, long animation
self.firstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 0.0f);
}];
[self.updateButton setTitle:#"make bigger" forState:UIControlStateNormal];
}else{
[UIView animateWithDuration:6.0f animations:^{ // equally long
self.firstIV.view.frame=CGRectMake(10.0f, 10.0f, 100.0f, 100.0f);
}];
[self.updateButton setTitle:#"update this" forState:UIControlStateNormal];
}
}
edit2
doesn't seem to be doing anything... will look into this layer.
-(void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
CGRect viewBounds = self.view.bounds;
self.myLabel.frame = CGRectMake(100.0f,viewBounds.size.height-20.0f,50.0f,50.0f);
}
You are only animating the frame of the yellow box, not the label. The frame of the label will not change just because its superview's frame has changed. You either need to:
Write code to animate both objects when the button is tapped
Write code to automatically move the label when the box is resized, typically by overriding your view controller's - (void)viewWillLayoutSubviews method.
If you're targeting iOS 6 only, you can use the new auto-layout feature. Ray Wenderlich has an excellent tutorial.

Resources