I am trying to apply background Image to all my screens in my iOs application.
My initial screen is a Registration screen where i want to apply the background image, the registration screen is as shown below:
I am using a custom UIView from xib file to apply the background image.
But after applying the background image it only shows the background image on the Registration screen and removes the login controls. I dont know why.
Following is the registration screen after applying the image background.
One more thing, I can see the background image only on the run time not in the Storyboard.
Following is my implementation
I have created one .xib file having a simple UIView, this UIView would serve as a master view in all my application. To obtain the UIView from nib file and to apply further settings I am using a custom UIView class. In this custom UIView class I am applying the background image on the UIView obtained from the nib file. As you can see above I am applying this custom UIView class to the Registration screen UIView. Following is the code as shown below:
class BackgroundMap: UIView {
var view: UIView!
override init(frame: CGRect) {
super.init(frame: frame)
view = loadViewFromNib ()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true
view.backgroundColor = UIColor(patternImage: UIImage(named: "MapWantd")!)
self.addSubview(view)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
view = loadViewFromNib ()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
view.translatesAutoresizingMaskIntoConstraints = true
view.backgroundColor = UIColor(patternImage: UIImage(named: "MapWantd")!)
self.addSubview(view)
}
func loadViewFromNib() ->UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: "BackgroundMap", bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
Related
I want to add and remove views at runtime in a stack view but am not able to do it.
Basically the situation is I am using a Button made from a Xib and adding it to a StackView, Stack View Distribution is fill equally and alignment is fill but the problem is that the button is not resizing according to the stack view.
I mean if I give initialiser a frame rect .zero then it's invisible, If I give it a CGRect(0,0,40,40) then it's a 40x40 square but don't resizes properly.
The main confusion and problem is it looks as expected in View Hierarchy Debugger but results on simulator don't translates in the same way attached some screenshots for reference and I also added some blank UIView and they are working as expected.
Code for rendering in ViewController
func render(viewModel: ViewModel) {
let rentalButton = MyCustomButton(frame: .zero)
let shiftingButton = MyCustomButton(frame: .zero)
rentalButton.titleText = "Rental button"
shiftingButton.titleText = "Shifting button"
rentalButton.subtitleText = "subtitle rental"
shiftingButton.subtitleText = "subtitle shifting"
for view in stackView.arrangedSubviews {
stackView.removeArrangedSubview(view)
view.removeFromSuperview()
}
let black = UIView()
black.backgroundColor = .black
let red = UIView()
red.backgroundColor = .red
stackView.addArrangedSubview(shiftingButton)
stackView.addArrangedSubview(black)
stackView.addArrangedSubview(rentalButton)
stackView.addArrangedSubview(red)
if viewModel.showRental && !viewModel.showHouseShifting {
rentalButton.showSubtitle = true
shiftingButton.showSubtitle = false
stackView.removeArrangedSubview(shiftingButton)
shiftingButton.removeFromSuperview()
}
else if !viewModel.showRental && viewModel.showHouseShifting {
rentalButton.showSubtitle = false
shiftingButton.showSubtitle = true
stackView.removeArrangedSubview(rentalButton)
rentalButton.removeFromSuperview()
}
stackView.layoutIfNeeded()
rentalButton.layoutIfNeeded()
}
Init Code for my Button from Xib
override init(frame: CGRect) {
super.init(frame: frame)
self._init()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self._init()
}
private func _init() {
Bundle.main.loadNibNamed("MyCustomButton", owner: self, options: nil)
self.addSubview(self.contentView)
self.contentView.frame = self.bounds
self.contentView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
setTitle(nil, for: .normal)
self.matchConstraints(toView: contentView)
decorate()
}
I usually use autolayout together with size classes, to optimize the UI for larger screens, like on iPad.
However, sometimes I need a completely different layout for the UI on iPad, still containing the same view controllers, but structured differently.
What is the best strategies in such situation, where autolayout and size classes are not enough? Loading a different storyboard for iPad?
Obviously the goal here is to not introduce any code duplication.
thanks
Loading a different storyboard for iPad? Obviously the goal here is to not introduce any code duplication.
If that's what you want to do, that feature is built-in and expected. You don't need any code at all. Just create your two storyboards and edit the Info.plist so that it has two main storyboard entries that point to them:
The right thing will just happen: on iPad, the second storyboard will load at launch.
I do same thing with size classes on UIViews and NSLayoutConstraints, for an example if i have different views for pad and phone, but i must do same logic for sections, i create two different views in same storyboard and enable/disable views based on size classes
Make Use of 2 Views(xib) with a single class file. The number of connections has to be same in both xibs but the placement of elements can be different for iPad and iPhone.
Here Below is my code to load XIB from a viewcontroller.
class ViewController: UIViewController {
var customView:CustomView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
loadCustomView()
}
func loadCustomView(){
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let window = appDelegate.window {
customView = CustomView(frame: CGRect(x: 0, y: 0, width: window.frame.width, height: window.frame.height))
window.addSubview(customView!)
}
}
}
Then there is the UIView Class
import UIKit
class CustomView: UIView {
#IBOutlet weak var lbl_Title: UILabel!
var view:UIView!
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setup()
}
func setup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = UIView.AutoresizingMask.flexibleWidth
view.autoresizingMask = UIView.AutoresizingMask.flexibleHeight
addSubview(view)
}
func loadViewFromNib() -> UIView {
if UIDevice.current.userInterfaceIdiom == .phone{
let bundle = Bundle(for:type(of: self))
let nib = UINib(nibName: "CustomView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}else{
let bundle = Bundle(for:type(of: self))
let nib = UINib(nibName: "CustomViewTab", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
}
You make the outlet connections to both the XIBs from this class and load the views depending on device type.
I have a scenario where in I need to create around 10 different prototypes of UITableViewCells. How ever all of these cells have some UI Elements (area marked in black) in common. And there is an area (marked in yellow) which is different for all these prototypes.
Is there a way I can abstract all the common UI Elements like the way it is done for contentView in UITableViewCell?
I tried to create a TableViewCell with all these elements and empty UIView to hold the customizations and planned to programatically load UIView (created in separate xib) into it.
The problem is I am not able to load the UIView into the UITableViewCell without loosing the constraints?
How to load a custom view with constraints into another?
Or Is there a way to create a custom UITableViewCell like the one in IB?
Create LoadableFromXibView subclass
Create a xib file, set the File's owner as your subclass
Drag outlets and design your view as usual
In your cell insert a subview that will be your reusable part of the cell and set its name as the new LoadableFromXibView subclass that you have created.
Source here: https://gist.github.com/DenHeadless/c3d682e7f499113109d6
class LoadableFromXibView: UIView {
var view = UIView()
override init(frame: CGRect) {
super.init(frame: frame)
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
func xibSetup() {
view = loadViewFromXib()
view.frame = bounds
view.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]
backgroundColor = .clearColor()
addSubview(view)
}
private func loadViewFromXib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil).first as! UIView
return view
}
}
So I wrote my own custom view with its own initializer. However, when my main view loads my custom view gets depicted in a wrong way. It takes bounds as 600x600 rectangle, while the superview is 375x607. I did try to put auto constraint, seems not to work. I tried to do it programmatically in the subview initialization, but whenever I try to initialize it's bounds property to its superview bounds I get nil in superview.
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
initPathLayer()
initHandleView()
initHandlePanGestureRecognizer()
layoutPathLayer()
layoutHandleViews()
}
I tried everything there is on the internet to make subview fill its superview, but I think that subview gets initialized before superview? Is that possible ? In the ViewController a have my custom view declared as an Outlet connection. I'm sure that the problem should be super easy and it's me who doesn't know the way Swift initializes the view.
Any ideas ?
Thank You.
Firstly, a Views init method is not the best place to perform a layout. The view could be resized at a later point which is typically always the case if the view is loaded from a Xib or you are creating it in a View Controllers viewDidLoad function.
That being said you have several approaches:
1. Use Auto Layout
Interface Builder
This can be done either in Interface Builder or programmatically. In Interface Builder you simply use the 'Pin' option and select all the sides of the view
When this is done you should be able to see your constraints in the Size inspector looking as follows:
Programmatically
Alternatively you can always add your constraints programmatically in your initializer:
override init(frame: CGRect) {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: frame)
let viewsDict = ["view": view]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view]-0-|", options: .allZeros, metrics: nil, views: viewsDict))
addSubview(view)
}
convenience required init(coder aDecoder: NSCoder) {
self.init(frame: .zero)
}
2. Manual Layout
Resizing Masks
For this you can either use resizing masks or you can control the frame in layoutSubviews. Resizing masks tell the system how the view should be sized relative to the superview:
override init(frame: CGRect) {
let view = UIView(frame: CGRectZero)
view.translatesAutoresizingMaskIntoConstraints = false
super.init(frame: frame)
view.frame = bounds
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(view)
}
Layout Subviews
Lastly, you can override layoutSubviews and go from there:
override func layoutSubviews() {
super.layoutSubviews()
view.frame = bounds
}
Is it possible to fix the position of a UITableViewController background image?
I've added the background image via the viewdidload method with the following code -
self.tableView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"blurredBGW"]];
But as the table is fairly long (and also contains static cells if that matters) it scrolls (and repeats) as you scroll down the table.
you can try to add the background image as UIView and not as UIColor, try this:
self.tableView.backgroundView = [[UIImageView alloc] initWithImage:
[UIImage imageNamed:#"blurredBGW.png"]];
for swift version 3.0.try this code.it will set a fixed background to a table view controller
self.tableView.backgroundView = UIImageView(image: UIImage(named: "background.png")!)
UIImageView *tempImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bg.png"]];
[tempImageView setFrame:self.tableView.frame];
self.tableView.backgroundView = tempImageView;
Make the background of your table (and the cells) transparent, and set the image for the view underneath the table.
for
swift 2.2
you may use the following line of code
self.tableView.backgroundView = UIImageView(image: UIImage(named: "xxxx.png"))
I prefer to configure background with creating its specific xib, because background can be difficult. I do next:
1) Create a class, that helps me to load nib
class UIXIBView: UIView {
var view: UIView!
func setupXIBWithName(_ nibName: String) {
view = self.getViewFromNibWithName(nibName)
self.addSubview(view)
view.frame = self.bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
}
func getViewFromNibWithName(_ nibName: String) -> UIView {
let bundle = Bundle.main
let views = bundle.loadNibNamed(nibName, owner: self, options: nil)! as [AnyObject]
let view = views[0] as! UIView
return view
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setupXIBWithName(String(describing: type(of: self)))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupXIBWithName(String(describing: type(of: self)))
}
}
2) I create your specific view, for example let it be LKBackgroundView.swift, and inherit it from UIXIBView
class LKBackgroundView: UIXIBView {
// You can add your own logic there or override smth
}
3) I create xib with the same name LKBackgroundView.xib
4) I set FileOwner's class to LKBackgroundView inside xib, don't make a mistake by setting LKBackgroundView to the view class
5) Configure you xib like you want
6) Initialize your view inside ViewDidLoad inside your View Controller
override func viewDidLoad() {
super.viewDidLoad()
LKBackgroundView(frame:tableView.bounds)
tableView.backgroundView = backgroundView
}
7) Test it
Xamarin/C# Solution
TableView.BackgroundView = new UIImageView(UIImage.FromBundle("blurredBGW"))