I made a a custom keyboard toolBar:
To do that I created a toolbar
let keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
and a view for the banner
adToolbar = GADBannerView(frame: CGRectMake(0, 44, self.view.bounds.size.width, 44))
then I grouped them in another UIToolbar (I tried UIView too)
let clusterView = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 88))
clusterView.addSubview(adToolbar)
clusterView.addSubview(keyboardToolbar)
and I added the view to the UITextField's keyboard.
Everything ok, but when I rotate the device happens this:
(clusterView UIToolbar resize correctly but not the two contained bars...)
I tried with
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
methodWhichGeneratesToolbar()
}
But it's the same, what can I try to solve this issue?
P.S.: I've made an example project.
Here's a suggestion on how to do it with programmatic autolayout using Visual Format Language (VFL). You'll need to have a look at the VFL docs to understand the VFL string syntax (They are pinning the outer view to the top and sides of the main view, and pinning the two subviews inside and to each other, and setting their height to 44).
I don't have AdMob installed, so I used a regular UIView instead of the banner view, but hopefully it should resize similarly - this code works ok on the 9.2 simulator in a test app
let keyboardToolbar = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 44))
keyboardToolbar.translatesAutoresizingMaskIntoConstraints = false //This is critical for all programmatic autolayout - if you forget it nothing will work
let adToolbar = UIView(frame: CGRectMake(0, 44, self.view.bounds.size.width, 44))
adToolbar.translatesAutoresizingMaskIntoConstraints = false
let clusterView = UIToolbar(frame: CGRectMake(0, 0, self.view.bounds.size.width, 88))
clusterView.translatesAutoresizingMaskIntoConstraints = false
//Map views to keys used in visual format language strings
let views = ["keyboardToolbar":keyboardToolbar,"adToolbar":adToolbar,"clusterView":clusterView]
//Map values to strings used in vfl strings
let metrics = ["barHeight":44]
//In named variables to make it clear what they are
//Syntax is explained in link above
let verticalConstraintsStr = "V:|[keyboardToolbar(barHeight)][adToolbar(barHeight)]|"
let adHorizontalConstraintsStr = "|[adToolbar]|"
let keyboardHorizontalConstraintsStr = "|[keyboardToolbar]|"
let subViewConstraintStrs = [
verticalConstraintsStr,
adHorizontalConstraintsStr,
keyboardHorizontalConstraintsStr
]
//Views must be added to subviews before adding constraints
// if the superview is referenced using
//the | symbol in the VFL strings
clusterView.addSubview(keyboardToolbar)
clusterView.addSubview(adToolbar)
//Converts strings to constraints for subviews and add them
for constraintStr in subViewConstraintStrs {
let allConstraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintStr, options: NSLayoutFormatOptions(rawValue: 0), metrics: metrics, views: views)
clusterView.addConstraints(allConstraints)
}
let clusterVerticalConstraintsStr = "V:|[clusterView]" //Note no | at the end - no bottom pin
let clusterHorizontalConstraintsStr = "|[clusterView]|"
view.addSubview(clusterView)
//Same process for the enclosing view
for constraintStr in [clusterVerticalConstraintsStr,clusterHorizontalConstraintsStr] {
let allConstraints = NSLayoutConstraint.constraintsWithVisualFormat(constraintStr, options: NSLayoutFormatOptions(rawValue: 0), metrics: metrics, views: views)
view.addConstraints(allConstraints)
}
VFL is powerful but annoying to debug, and can't do all types of constraint (e.g. not alignments - you have to use an even more verbose API for those).
Related
I am facing an issue with scrollView. I have added scrollView in my storyboard as
View
- MainView
- ScrollView
- accountSavingSubView
Now I am trying to add views in accountSavingSubView as below
//MARK:- Create and add wallets on the UI
private func createAndAddWallets()
{
var yPosition : CGFloat = 0.0
//1. Create left hand side wallets with help of keys
for key in dictData.keys
{
let walletView:Wallet = Bundle.main.loadNibNamed("Wallet", owner: self, options: nil)?.first as! Wallet
walletView.translatesAutoresizingMaskIntoConstraints = false
walletView.frame = CGRect(x: 0.0, y: yPosition, width: accountSavingSubView.frame.width, height: accountSavingSubView.frame.height)
walletView.accountNameLabel.text = key
accountSavingSubView.addSubview(walletView)
yPosition += walletView.frame.maxY
walletView.isUserInteractionEnabled = true
print("walletView frame is :\(walletView.frame)")
}
//set continer scroll content size
self.contanerScrollView.contentSize = CGSize(width: self.accountSavingSubView.frame.width, height: CGFloat(80.0/320.0)*UIScreen.main.bounds.width*CGFloat(self.dictData.keys.count))
}
Where dictData is a dictionary of ([String: String]).
But in my view, I can see only last view is added on (x=0, y=0). ScrollView content size is correct & I can scroll without having views except last one at first place(x:0,y:0).
Please let me know what am I doing wrong here in my code while adding views.
You need to comment this
walletView.translatesAutoresizingMaskIntoConstraints = false
as it's used with setting constraints only ,and verify that the supplied frame values here are not 0
walletView.frame = CGRect(x: 0.0, y: yPosition, width: accountSavingSubView.frame.width, height: accountSavingSubView.frame.height)
btw you can also comment it as the frame will be captured as you set it in xib if you make it freedom size
The answer can be for swift, objective-c or c# even if I'm using xamarin.
I have issues creating scrollview in the storyboard with xamarin, I followed several tutorial for xcode but I can't do what I need.
Then I removed the scrollview and just have a long vertical uiview and actualy added some code to scroll it using pangesture moving the uiview frame.
But I know it would be better to use srollview also I though that it must be possible to insert a scrollview before the view (in code) to achieve the same thing.
So is it possible ?
Yes it is possible, for example you could do this in your ViewController by adding the ScrollView as a subview to your View.
override func viewDidLoad() {
super.viewDidLoad()
let scrollViewFrame = view.bounds
let scrollView = UIScrollView(frame: scrollViewFrame)
scrollView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
scrollView.backgroundColor = UIColor.red
view.addSubview(scrollView)
let innerView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
innerView.backgroundColor = UIColor.blue
scrollView.addSubview(innerView)
scrollView.contentSize = CGSize(width: scrollViewFrame.width, height: scrollViewFrame.height * 2)
}
In this example the ScrollView has the same size as the main View. You can adjust this by changing the scrollViewFrame constant.
The innerView has a fixed size of 50x50. If it should cover the entire scrollView, just use the scrollViewFrame and the same autoresizingMask.
I adjusted the contentSize to be twice as high as the scrollView in order to make it scroll. This might or might not be necessary for your solution.
I'm trying to draw a a line at the top of a table view as follows:
func addTopLineToTableView() {
let layer = CALayer()
layer.backgroundColor = UIColor.green.cgColor
layer.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: 1.0)
view.layer.addSublayer(layer)
}
This works perfectly on every device except an iPhone X.
I've tried getting the safeAreaInsets and the safeAreaLayoutGuide but that didn't seem to do anything.
Any help greatly appreciated!
EDIT
Above is what the end result should look like. Here the table view has a red background and my green CALayer is right at the top as expected. It has been placed there using the function above. This is the way it looks on every device except the iPhone X.
Here is what happens when the same code is run on an iPhone X.
Like friends said before, the line can be set to the parts, you can extend the view
extension UIView {
#IBInspectable var topBorderWidth: CGFloat {
get {
return 0.0 // Just to satisfy property
}
set {
let line = UIView(frame: CGRect(x: 0.0, y: 0.0, width: bounds.width, height: newValue))
line.translatesAutoresizingMaskIntoConstraints = false
line.backgroundColor = UIColor.green
self.addSubview(line)
let views = ["line": line]
let metrics = ["lineWidth": newValue]
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "|[line]|", options: [], metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[line(==lineWidth)]", options: [], metrics: metrics, views: views))
}
}
}
Such use
testView.topBorderWidth = 1
Hello
I'm trying to put an Image as a background for my ViewControllers, guiding myself for other posts I found this way:
I created the following extension:
extension UIView {
func addBackground() {
// screen width and height:
let width = UIScreen.mainScreen().bounds.size.width
let height = UIScreen.mainScreen().bounds.size.height
let imageViewBackground = UIImageView(frame: CGRectMake(0, 0, width, height))
imageViewBackground.image = UIImage(named: "msa_background")
// you can change the content mode:
imageViewBackground.contentMode = UIViewContentMode.ScaleAspectFill
imageViewBackground.clipsToBounds = true
self.addSubview(imageViewBackground)
self.sendSubviewToBack(imageViewBackground)
}
}
I'm calling this extension in every ViewController with the following code:
override func viewDidLoad() {
super.viewDidLoad()
self.view.addBackground()
}
The problem is when I rotated the screen the image in the background don't fill all the space in the ViewController, I have checked any possible solution that I found but I can't find a way to do it.
I really appreciate any help from you
You are adding the image with the current frame of the screen and never changing it. When you rotate the device it will keep the same frame.
Change it to use AutoLayout like this...
extension UIView {
func addBackground(imageName: String, contentMode: UIViewContentMode) {
let imageViewBackground = UIImageView()
imageViewBackground.image = UIImage(named: imageName)
// you can change the content mode:
imageViewBackground.contentMode = contentMode
imageViewBackground.clipsToBounds = true
imageViewBackground.translatesAutoresizingMaskIntoConstraints = false
self.insertSubview(imageViewBackground, atIndex: 0)
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[imageViewBackground]|",
options: [],
metrics: nil,
views: ["imageViewBackground", imageViewBackground]))
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[imageViewBackground]|",
options: [],
metrics: nil,
views: ["imageViewBackground": imageViewBackground]))
}
}
The auto layout constraints will then make the image view fit to the view no matter how the orientation of the device or frame of the view changes.
Video of the issue!
Example of what I mean by Twitter-like UIScrollView:
I basically have it working, but I have this small glaring issue and I don't know where it is coming from. I have checked all the constraints and values for my two view controllers, but something is off.
In short,
The code that creates the NavBar and then populates it with the two ViewControllers side by side:
override func viewDidLoad() {
super.viewDidLoad()
var navBar: UINavigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.bounds.width, 64))
navBar.barTintColor = UIColor.blackColor()
navBar.translucent = false
//Creating some shorthand for these values
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
// This houses all of the UIViews / content
scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.clearColor()
scrollView.frame = self.view.frame
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.bounces = false
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.size.width * 2, height: hBounds)
//Putting a subview in the navigationbar to hold the titles and page dots
navbarView = UIView()
//Paging control is added to a subview in the uinavigationcontroller
pageControl = UIPageControl()
pageControl.frame = CGRect(x: 0, y: 35, width: 0, height: 0)
pageControl.pageIndicatorTintColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.3)
pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
pageControl.numberOfPages = 2
pageControl.currentPage = 0
self.navbarView.addSubview(pageControl)
//Titles for the nav controller (also added to a subview in the uinavigationcontroller)
//Setting size for the titles. FYI changing width will break the paging fades/movement
navTitleLabel1 = UILabel()
navTitleLabel1.frame = CGRect(x: 0, y: 8, width: wBounds, height: 20)
navTitleLabel1.textColor = UIColor.whiteColor()
navTitleLabel1.textAlignment = NSTextAlignment.Center
navTitleLabel1.text = "Title 1"
self.navbarView.addSubview(navTitleLabel1)
navTitleLabel2 = UILabel()
navTitleLabel2.alpha = 0.0
navTitleLabel2.frame = CGRect(x: 100, y: 8, width: wBounds, height: 20)
navTitleLabel2.textColor = UIColor.whiteColor()
navTitleLabel2.textAlignment = NSTextAlignment.Center
navTitleLabel2.text = "Title 2"
self.navbarView.addSubview(navTitleLabel2)
//Views for the scrolling view
//This is where the content of your views goes (or you can subclass these and add them to ScrollView)
feedViewController = storyboard?.instantiateViewControllerWithIdentifier("FeedController") as FeedViewController
view1 = feedViewController.view
addChildViewController(feedViewController)
feedViewController.didMoveToParentViewController(self)
view1.frame = CGRectMake(0, 0, wBounds, hBounds)
self.scrollView.addSubview(view1)
self.scrollView.bringSubviewToFront(view1)
//Notice the x position increases per number of views
secondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondController") as SecondViewController
view2 = secondViewController.view
addChildViewController(secondViewController)
secondViewController.didMoveToParentViewController(self)
view2.frame = CGRectMake(wBounds, 0, wBounds, hBounds)
self.scrollView.addSubview(view2)
self.scrollView.bringSubviewToFront(view2)
navBar.addSubview(navbarView)
self.view.addSubview(navBar)
}
I've looked at my storyboard and both ViewControllers seem identical in regards to their constraints.
I know this is an issue because both ViewControllers are populated by UITableViews. When I scroll through the SecondViewController, it works perfectly. When I scroll through the FeedViewController, there is a small white space at the top that I can't seem to get rid of and it shows that the text cuts off there. I've been stuck on this for a long time and if there is any other information needed, I'll gladly provide it.
Edit: Included video of the issue. If I could, I would bounty this question right now. I don't understand the cause
Update: After swapping both ViewController positions, I have noticed that the problem does not lie with either ViewController. The problem lies with page 1 being set lower. When swapped, the original SecondViewController also experienced the same behavior
So, I think everyone who implements this runs into this issue at some point. The issue isn't with the first ViewController. Simply adjust the constraint to be 44 from the top. The issue is with the second ViewController and it isn't so much an issue when you understand how they work. Technically, it is off to the side and hence its top constraint does not adhere to the Navigation Bar, so what you have is a constraint - 20. Which, depending on how you originally placed your constraints, can give you this seeming issue.
But basically, anyone and everyone will run into this issue when implementing this.
TL;DR: To make everything seamless, your second, third, fourth, fifth, etc. page View Controllers need a constraint + 20 of your first View Controller. With my set-up, I use a constraint of 44 for my first View Controller and hence 64 for the second