Image Fit Any Screen - ios

Hi I am making a simple login screen for my app in xCode and want to make sure that it fits any screen
import UIKit
class LoginController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// will allow me to put a image as my UI background instead of a color
self.view.backgroundColor = UIColor(patternImage: UIImage(named:"BestBackground")!)
}
}
This successfully loads the image but i just want to make sure it will load on any screen size from iPhone 6 to iPhone 6 Plus

Don't set the background to a pattern. Just add a UIImageView behind all the other views. Set its contentMode to scaleAspectFill and set its image to your "BestBackground" image. You can do all of this is the storyboard or in code.

Related

UINavigationBar background image with scaleAspectFill-like behavior

tl;dr
I'm trying to set a background image for a UINavigationBar. Is there a way to configure the navbar so that it uses scaleAspectFill-like behavior (as a UIImageView with contentMode set to scaleAspectFill)?
The goal
I have an image roughly fitting the navbar dimension of a landscape iPhone (64pt height, about 800pt width; the exact dimension should not matter here though), which I set with navigationBar.setBackgroundImage(img, for: .default). The image is a blurred photo, so no need for pixel-perfection. Btw -- I'm using UINavigationBar.appearance to set the background for all navbars at once.
What I'd like it to do: show the center part of the image when in portrait orientation, and show the full image without cropping when in landscape orientation. If the height doesn't match up exactly (as is to be expected on iPhone X) it should scale up a bit to fill the additional height.
As fallback solution I'd also accept if an image of lesser width is shown centered and fills up the additional horizontal space by stretching the first and last pixel column.
What I tried so far
Goal 1: setting the contentMode of UINavigationBar -- seems to be ignored when rendering the image
Goal 2: using a smaller image and making it strechable with stretchableImage(withLeftCapWidth:topCapHeight:) -- fills the navbar but the image is pushed to the right bottom (apparently because this method only considers left and top as strechable margins)
Goal 2: using a smaller image and making it resizable with resizableImage(withCapInsets:resizingMode:) with resizingMode set to stretch -- in landscape it just stretches the image to full width, ignoring the cap insets (probably this method does not do what I think).
Solved by adding an image view to the UINavigationController's view, as described here. The image view is attached to the top, left and right of the nav controller's view and to the bottom of the navbar.
In contrast to the example in the article, I don't use a custom navigation bar; instead I insert the image view behind the existing navbar. Note that the navbar has to be transparent to make this work. Also, the image view has to be clipped, else it strangely extends beyond the navbar's bottom.
Maybe this can be of help to someone else, so here's some code (this should only be applied once to a nav controller, so maybe you are better off putting this code in a subclass than in an extension):
extension UINavigationController {
func setBackgroundImage(_ image: UIImage) {
navigationBar.isTranslucent = true
navigationBar.barStyle = .blackTranslucent
let logoImageView = UIImageView(image: image)
logoImageView.contentMode = .scaleAspectFill
logoImageView.clipsToBounds = true
logoImageView.translatesAutoresizingMaskIntoConstraints = false
view.insertSubview(logoImageView, belowSubview: navigationBar)
NSLayoutConstraint.activate([
logoImageView.leftAnchor.constraint(equalTo: view.leftAnchor),
logoImageView.rightAnchor.constraint(equalTo: view.rightAnchor),
logoImageView.topAnchor.constraint(equalTo: view.topAnchor),
logoImageView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)
])
}
}
You might try adding the UIImageView with contentMode = .scaleAspectFill, directly inside the UINavigationBar, doing so:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let imgView = UIImageView(image: UIImage(named: "desert"))
imgView.contentMode = .scaleAspectFill
imgView.frame = self.navigationController!.navigationBar.frame
self.navigationController?.navigationBar.addSubview(imgView)
self.navigationController?.navigationBar.sendSubview(toBack: imgView)
}
}
the result is:

What are the correct dimensions for a custom tab bar item background image?

So I have a .png that's 428x176 px:
I want to use it as a tab bar background image for when an item is in the selected state. The problem is that the image is too big. So now, I'm exploring the different resolution suffixes: #1x, #2x, #3x. But regardless of which I append to the image filename, the image remains too big for the tab bar item's background, although it does get smaller as I increase the resolution factor. How do I determine what the correct dimensions for this image should be?
Keep in mind that this is a background image for when any particular UITabBarItem is in the selected state. It is meant to be the entire width and height of the UITabBarItem. My tab bar will have three items laid across the width of the screen.
Update:
A lot of answers are providing swift based solutions, but what I'm asking for is what dimensions my image should be in pixels, in other words, what sized images should I be including in my bundle, so that the image will fit in the tab bar at all screen sizes. I imagine that since a UITabBar by default expects a UIImage for its selectionIndicatorImage field and not a UIImageView that there must be a solution that doesn't involve hacking around the UITabBar and adding a UIImageView as a subview of it and managing what position the image view should be in, manually, based on which UITabBarItem is currently selected.
Making my answer for this question how to make UITabBar selection indicator image fill the whole space? as reference:
Subclass the UITabBarController
It works for multiple devices even with rotation.
Notes:
Make your images' rendering mode as Original.
Assign this class below to your UITabBarController in your Storyboard or as your base class if you're doing your screen programmatically.
//
// BaseTabBarController.swift
// MyApp
//
// Created by DRC on 1/27/17.
// Copyright © 2017 PrettyITGirl. All rights reserved.
//
import UIKit
class BaseTabBarController: UITabBarController {
let numberOfTabs: CGFloat = 4
let tabBarHeight: CGFloat = 60
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateSelectionIndicatorImage()
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
updateSelectionIndicatorImage()
}
func updateSelectionIndicatorImage() {
let width = tabBar.bounds.width
var selectionImage = UIImage(named:"myimage.png")
let tabSize = CGSize(width: width/numberOfTabs, height: tabBarHeight)
UIGraphicsBeginImageContext(tabSize)
selectionImage?.draw(in: CGRect(x: 0, y: 0, width: tabSize.width, height: tabSize.height))
selectionImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
tabBar.selectionIndicatorImage = selectionImage
}
}
There's no clear cut answer for this as the tab bar itself isn't of fixed size on various iOS versions and various iOS devices.
Starting iOS 11, tab bar will have smaller height and titles will move to right in landscape mode.
So the idea is to have a stretchable image that can be stretched along all all edges - top/leading/bottom/trailing.
For your reference -
http://macoscope.com/blog/stretchable-images-using-interface-builder/
Here are few images from above blog post to help make it clear -
Hope this helps.
Use the below two lines.
self.tabBarController.tabBar.autoresizesSubviews = NO;
self.tabBarController.tabBar.clipsToBounds = YES;
Xcode works with point, not pixels, so the width will always be 320. In the case of retina display one point is 2x2 pixels and in normal mode it is 1x1.
I think the height for the tab bar should be 320x49 for normal and 640x98 for retina.
the retina image should have the same name as the normal one with the #2x at the end.
Rename your image to tabbarBack#2x.png. This is called pixel doubling for the Retina Display.
Without the #2x iOS doesn't know that it should apply a scale factor and it will be used as it is and though it should be halved.
tabbarBack.png (45 px or so)
tabbarBack#2x.png
[[[self tabBarController] tabBar] setBackgroundImage:[UIImage imageNamed:#"tabbarBack.png"]];
Have you tried with adding imageView as subview of tabbar
var bgView: UIImageView = UIImageView(image: UIImage(named: "background.png"))
bgView.frame = CGRectMake(0, 420, 320, 60)//you might need to modify this frame to your tabbar frame
self.view.addSubview(bgView)
If we talk about height & width of tabbar ,it is 320*49 / 640*98 #2x for standard tabbar. so if you want to keep same dimension try with these width/height ratio.
To add to Tarun Tyagi's answer:
If you add the image to the asset store you can also do visual slicing with the system tools.
Create an asset catalog
Drag & Drop your image into the asset catalog.
Select that image in the asset catalog and click on the "Show Slicing" button on the bottom right
Click on "Start Slicing" and slice that image
Try resizing the image to the tab bar size. Or Add an imageView to the tabBar as subview, and then use the image in that imageView.
Subclass the TabBarController and add imageview there:
class YourTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let backgroundImage = UIImageView(image: UIImage(named: "gray background"))
backgroundImage.frame = backgroundImage.bounds
self.view.addSubview(backgroundImage)
}

Swift3 center UIbutton

ok, I have added a button to the .swift storyboard, I have then added the following code to my ViewController.swift
#IBOutlet weak var HelloButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
HelloButton.frame.origin.x = view.frame.size.width / 2
}
However when I test it on my iPhone 5s it moves it to the right of my screen and does not make it centered. I am unsure what I need to do to make the button centered.
Yes this is my first helloWorld app, and maybe over my head, but I understand web programing, and I understand to make it center automaticilly I need to have the screen view width total and the button width. I then need to say to the button to divide the screen view width by 2.
for example in CSS we do this
#button{margin:0 auto;}
but this is not HTML or CSS. IT'S SWIFT3
Try this:
HelloButton.center.x = view.frame.width/2
Yours is not correct because the origin.x is the first point (top left) of the button view, not the center of the view, you can change it's background color to different color to see it
Also, the variable name should be lowerCase, followed by code convention
You should be using Autolayout for accomplishing what you want and the way you do is like the following in your ViewController inside of Main.storyboard: (control + left mouse click -> dragging the blue line upwards and then releasing the control and left mouse click, you will see the popup)
In the popup select the options:
Center Horizontally In Container
Center Vertically in Container
But if you want to do it programmatically, do it in viewWillLayoutSubviews method:
class ViewController: UIViewController {
#IBOutlet weak var button: UIButton!
override func viewWillLayoutSubviews() {
super. viewWillLayoutSubviews()
button.center = view.center
}
}
In your project Autolayout is enabled? if Auto Layout enabled then Change UI in viewdidLoad: may not give appropriate result. use -viewWillLayoutSubviews method to do UI task, it you want to center your button
myButton.center = self.view.center
Hope it will work.
Add the code to viewDidAppear that way the actual view has shown up. Also, you should use button.center.x instead of button.frame.origin.x if you want to center it

UIView Not Changing Size On Rotation Autolayout

I have a UIScrollView with a UIView for content inside of it. On the UIView, I have some buttons I'm using for a menu. Everything works great until I rotate to landscape. Once I rotate, the buttons on the UIView can't be clicked but the top buttons still work. I'm assuming it's because the UIView holding the buttons isn't being resized so it's not responding to taps. This can be seen in the 3d exploded image (Image 4). I have tried all of these lines of code to try to get it to resize, buttons have worked (code is commented because I tried different combos).
override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
//hides and displays tab bar on orientation changes
if toInterfaceOrientation.isLandscape {
tabBarController!.tabBar.hidden = true
//self.navigationController!.view.sizeToFit()
//self.navigationController!.view.setNeedsLayout()
//self.viewForContent.setNeedsLayout()
//self.viewForMenuItems.setNeedsLayout()
//self.viewForContent.contentSize = CGSizeMake(900, 900)
//self.viewForContent.setNeedsLayout()
}
else if toInterfaceOrientation.isPortrait {
tabBarController!.tabBar.hidden = false
//self.navigationController!.view.setNeedsLayout()
//self.viewForContent.setNeedsLayout()
//self.viewForMenuItems.setNeedsLayout()
//self.viewForContent.contentSize = CGSizeMake(900, 900)
//self.viewForContent.setNeedsLayout()
}
}
Also I have this in the DidLoad and DidLayoutSubviews. When I comment out the DidLayoutSubviews the buttons work again, but the scrollview isn't large enough to allow me to scroll to the bottom buttons when in landscape
override func viewDidLoad() {
super.viewDidLoad()
let size = UIScreen.mainScreen().bounds.size
viewForContent.contentSize = CGSizeMake(size.width, 458)
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let size = UIScreen.mainScreen().bounds.size
viewForContent.contentSize = CGSizeMake(size.width, 458)
}
The My Information, My Staff, My Colleagues, and My Residents are the buttons that work in landscape mode.
I have used autolayout constraints to setup the views. Also, I am using XCode7 with Swift.
Any help would be appreciated
Thanks
First I setup my views as outlined in the link posted above in the comments by Kusul (Thank you). But when I did this and added the buttons to the content view they wouldn't show on when the screen was rotated to landscape because they were off of the bottom and the UIScrollView didn't "grow".
I figured out that I needed to add a bottom constraint to my last button. So the last button now has a constraint to the top and the bottom. This forced the scroll view to "grow" to the proper size. I could then eliminate setting the size in the didLoad and didLayoutSubviews. Thanks for the help

UIImageView loading then immediately turns black

So I have been going through my code over and over and searching online for a solution to this problem and I am stuck. Nothing that I can find online has any information on why the screen would load and then turn black. The code modally presents another view that has a UIImageView inside of it. There is an image (.png file) that should be loading in the image view and you can see the image while the view is being presented modally, but as soon as the view slides into place the whole view turns black. Also you can see the image in the storyboard and it shows it correctly, it is only in the simulator where the image immediately turns black.
Here is the code of the view controller that is presented:
import UIKit
class MenuViewController: UIViewController
{
#IBOutlet var imageView: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view
let backgroundImage: UIImage! = UIImage(contentsOfFile: "behind_alert_view.png")
imageView.image = backgroundImage;
}
}
I also tried using the function UIImage(named: "behind_alert_view.png"). Any help would be greatly appreciated!
First, in your SecondViewController, set the main view's background color to clearColor, and add a second full-size view inside it. Set the second view's background color to your desired color, for example "darkGrayColor" and set it's alpha to 0.3.
Then, inside of your Interface Builder, click on the segue between FirstViewController and SecondViewController and under for "Presentation" choose "Over Current Context".
Alternatively, the following code should do the same thing. But in Xcode 6 Beta 7, I could not get it to work successfully:
self.modalPresentationStyle = UIModalPresentationStyle.CurrentContext

Resources