Swift: Unexpected zoom in ad - ios

I added the MoPub banner ad with the following code:
self.adView.delegate = self
self.adView.frame = CGRectMake(0, self.view.bounds.size.height - MOPUB_BANNER_SIZE.height,
screenWidth, MOPUB_BANNER_SIZE.height)
self.view.addSubview(self.adView)
self.adView.loadAd()
For some reason when I run the application the screen doesn't appear fully, but it has a weird zoom. What could be happening?
UPDATE:
I moved the frame of the adView to viewDidLayoutSubviews() like this:
override func viewDidLoad(){
self.adView.delegate = self
self.view.addSubview(self.adView)
self.adView.loadAd()
}
override func viewDidLayoutSubviews() {
var screenSize: CGRect = UIScreen.mainScreen().bounds
var screenWidth = screenSize.width
self.adView.frame = CGRectMake(0, self.view.bounds.size.height - MOPUB_BANNER_SIZE.height,
MOPUB_BANNER_SIZE.width, MOPUB_BANNER_SIZE.height)
}
But the error persists, I'm using Auto Layout and Size Classes.

The frame properties that are present in viewDidLoad do not reflect the actual frame properties of the view that will be presented on screen. In this method the outlets have been created and all that, but not layed out yet.
Therefore it is the correct place to create an AdView, setting its properties...
BUT the layout has to be done somewhere else - the best place is viewDidLayoutSubviews. If this method is called you can be sure that the frame properties are set correctly.
Before changing anything in your code you can just log the frame in viewDidLoad and log them in viewDidLayoutSubviews - they should be different. Then you can go ahead and move your layout logic over there as well.

I fixed this issue by myself. When working with Spritekit and scenes, remember to always configure the scene before presenting it, specially the scene's ScaleMode. After setting the scene's scaleMode to: scaleMode= .Fill the zoom stopped happening.

Related

iOS part of viewcontroller goes black when orientation changes

When I change viewcontroller orientation while it's loading on particular orientation, part of the screen goes blank at the bottom.
Steps to reproduce:
Have tableview(Not necessary I think. could be any view) in a view controller
Push a new viewcontroller which also has a tableview(Not necessary I think. could be any view) during
an action
Rotate the screen from one orientation to Another(Portrait to landscape or landscape to portrait).now you will be able to see the dark part.
Press back button to pop the current viewcontroller
Now rotate from one orientation to Another(Portrait to landscape or landscape to portrait).now you will be able to see the dark part here as well.
The bottom part of viewcontroller goes black.
I am able to reproduce 7 out of 10 times.
FYI:
My Viewcontroller has only tableview that's all and all cells has autolayout constraints.
Trying to understand why it happened and I would like to fix it.
More details on reproducing the issue:
Basically you can reproduce this issue only if you hold your device in slanting position and either push or pop a view controller and while the page is trying to load , you have to change the orientation then the issue happens.
(Pun intended.... lol )
In other words ,you have to tilt you device as if you are steering the wheel :).
This issue can only happen if you use your device like a steering wheel :)
In your viewWillTransition method call layoutIfNeeded()
Try to reset the layout of TableView after rotation by using this method,
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.layoutIfNeeded()
}
or
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.layoutIfNeeded()
}
I also experienced the same issue.
My view controller had just one tableview and when we tilt , I mean change the orientation during a navigational push or pop this used to happen.
Try setting the frame
in viewDidLayoutSubviews
-(void)viewDidLayoutSubviews{
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat width = CGRectGetWidth(screen);
CGFloat height = CGRectGetHeight(screen);
CGRect frame = self.tableView.frame;
// To check if there's any mismatch between the sizes of screen and tableview then set the tableview frame same as screen frame.
if (CGRectGetHeight(frame) != height && CGRectGetWidth(frame) != width) {
self.tableView.frame = CGRectMake(0, 0, width, height);
}
}
or try setting the frame in viewWillTransitionToSize
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
CGRect screen = [[UIScreen mainScreen] bounds];
CGFloat width = CGRectGetWidth(screen);
CGFloat height = CGRectGetHeight(screen);
CGRect frame = self.tableView.frame;
if (CGRectGetHeight(frame) != height && CGRectGetWidth(frame) != width) {
self.tableView.frame = CGRectMake(0, 0, width, height);
}
}
FYI:
if setting the frame self.tableView.frame = CGRectMake(0, 0, width, height); doesn't work then you could try [self.tableView layoutIfNeeded]
Set frame again in
viewDidLayoutSubviews
In one view controller(Parent the one that pushes new view controller) setting the frame at viewDidLayoutSubviews solves the issue in another view controller(The child view controller , one that's getting pushed) setting the frame at viewWillTransition fixes the problem :) But still I don't know why there's no universal solution to this problem.

Where is the proper place to set contentOffset in UIScrollView sub-class?

I am working on a view which inherits from UIScrollView, and the requirement is that it should start at a contentOffset.y position that is dependent on the view size. Specifically I want to start one screen down in a content that is 3 x the view height.
Like this:
- (void)configureStartCondition {
self.contentSize = CGSizeMake(self.bounds.size.width, self.bounds.size.height * 3.0);
self.contentOffset = CGPointMake(0.0, self.bounds.size.height * 1.0);
}
The view itself is wired up with constraints in Storyboard, just like any view. As it works, the framework will initially give the view the size it has in the storyboard, then when the device size is known, the view's size will be changed to its final size. This is how it should work, and I am fine with this. My question is where do I call configureStartCondition?
An obvious solution would be to put this code in setFrame:, but it doesn't work. setFrame: is only called for the initial frame size, which might or might not be the final size. Why is this?
// NOT working
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
[self configureStartCondition];
}
A more common place would be in layoutSubview, where I usually do this kind of setup. However, as it is a UIScrollView the layoutSubview is called very frequently as the user scrolls the view. Meaning I would need to save the last height and compare it to make things work, then run through this test millions of times just to be able to initialize. It feels like a kludge to me.
// Working, but ugly
- (void)layoutSubview {
[super layoutSubviews];
if (self.bounds.size.height != self.savedHeight) {
self.savedHeight = self.bounds.size.height;
[self configureStartCondition];
}
// Do layout stuff
}
Another place that may seem good is setBounds:. It will get called for the view size change, but since the contentOffset property is tied to the bounds property, I actually get as many calls here as to layoutSubviews.
So, is there a better place to do it, or a better way to do it?
Side issue, less important in my case, but can the content offset be set from a storyboard?
EDIT: Solutions in Swift are also fine.
setContentSize: works perfect for me. It called only when size changed.
PS: My code to check: (sorry for swift but UIKit make no difference)
class CustomScrollView: UIScrollView {
override var contentSize: CGSize{
didSet{
var offset = contentOffset
offset.y = contentSize.height / 2.0
contentOffset = offset
}
}
}

Make use of safe areas in Objective C

I am working to migrate my app so I can use it on iPhoneX without scaling. Currently I layout a subview with the following code:
CGRect frame = self.view.bounds;
frame.size.height = 295;
frame.origin.y = 20;
This works fine until you add in the notch. I am not seeing a clear way to find out what the frame.origin.y should be set to, without just hard coding.. Which I would like to avoid.
What I would like to do is this - frame.origin.y = 20 + self.view.safeAreaInserts.top
But that doesn't do a thing.
Thanks!
Perhaps safeAreaInsets is being changed after your code runs (for example if you're trying to perform layout in viewDidLoad). You need to override safeAreaInsetsDidChange to be notified when it changes.
Note that safeAreaInsetsDidChange is a method of UIView, not UIViewController, so you'll need to create and use a subclass of UIView to override the method. It would probably be sufficient to override it like this:
override func safeAreaInsetsDidChange() {
super.safeAreaInsetsDidChange()
setNeedsLayout()
}
And then you can set the subview's frame in layoutSubviews.
Or you could just use constraints instead of setting the frame directly, and let auto layout handle it all for you.

Swift LayoutSubviews moving all subviews every user interaction

I apologize if this question has been already addressed, but I cannot find it.
I thought it would be simple enough to animate a UIImageView from offscreen onto the screen at certain points in my program. So I created the UIImageView and coded it up, but am getting a lot of strange behaviour. I have narrowed it down to the fact that layoutSubviews is getting called every user interaction (every button press) and it is resetting the size and location of all my items. I am using auto layout for every screen item save this one, so that is fine for the things I want auto layed out. But this one item is constantly being moved back to it's starting location. I proved it this way:
override func viewDidLoad() {
super.viewDidLoad()
print("viewDidLoad")
let screenWidth = UIScreen.mainScreen().bounds.width
let screenHeight = UIScreen.mainScreen().bounds.height
print(image.center)
image.center = CGPointMake(screenWidth / 2, image.bounds.height / -2)
print(image.center)
}
override func viewWillLayoutSubviews() {
print("viewWillLayoutSubviews")
print(image.center)
}
override func viewDidLayoutSubviews() {
print("viewDidLayoutSubviews")
print(image.center)
}
This is the result I get:
viewDidLoad
(294.0, 228.0)
(160.0, -64.0)
viewWillLayoutSubviews
(160.0, -64.0)
viewDidLayoutSubviews
(294.0, 228.0)
I don't have any constraints attached to this one view. Thoughts? I guess I could store the location and constantly move it around manually, but I'm sure there is a switch somewhere that turns off the autolayout for specific items. I just can't find it.
Thanks for your help...
The image must have autoresizingMasks that are getting translated into constraints.
Disable it by doing this:
image.translatesAutoresizingMaskIntoConstraints = false

How to programmatically change UIView's frame inside an UIView Subclass?

I have a UICustomButton(subclass of UIButton) in the interface builder. I want change this button's frame inside UICustomButton.
I tried following code:
// make it 10 points wider and higher
CGRect newFrame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width + 10, self.frame.size.height + 10);
self.frame = newFrame;
in awakeFromNib:, drawRect:
none of them worked, the result is unchanged.
Don't do that! That's bad architecture!
A view should never change it's own frame. It's always the parent view resizing its children.
If you detect you need another frame inside your view: send out a delegate call to the parent view and change the frame in there. the delegate method could look like this
- customButton:(UIButton *)button requestNewFrame:(CGRect)frame
{
button.frame = newFrame;
// some storing method so you remember the frame on rotations and stuff
}
Just as a reminder: child views are always resized in
- (void)layoutSubviews //UIView
- (void)viewDidLayoutSubviews //UIViewController
That makes your code safe for different UIInterfaceOrientations and different devices.
Neither awakeFromNib nor drawRect: is appropriate.
You can change its frame in layoutSubviews, but you might run into trouble if your app uses auto layout.
I'd put it in viewDidLoad. If you put it in a recurring method, it will increase in size each time it's called.

Resources