AutoLayout Constraints Across All Devices Mathematically - ios

I am new to IOS Development, please go easy.
I am attempting to create a universal identically positioned UIView in the main ViewController of any IOS Device (The view will change dynamically and match across all IOS devices (relative to the size of the screen), and not ruin the positioning of the view). Basically, is it possible to mathematically alter the "equalToConstants" in the anchor to create some kind of formula that will yield the same result across all screens using division/multiplication of the main screen size (IPad, IPhone, etc.) to determine an exact similar location visually?
Something like bottomButtonView.widthAnchor.constraint(equalToConstant: -/+ 20).isActive = true
As opposed to a "fixed" variable that yields a different "visual" position across all IOS devices:
bottomButtonView.widthAnchor.constraint(equalToConstant: 400).isActive = true
I do not want to use the storyboard, as I am knee-deep in programmatically building my app. I have also heard programmatically programming your app is the way to go for easier future changes etc.
The following snippet of code is showing what I have in the ViewController that displays a red rectangle which would be great to have the same size/shape relative to the screen size (IPad, IPhone 6,7,8,X,11, etc.):
let bottomButtonView = UIView()
bottomButtonView.translatesAutoresizingMaskIntoConstraints = false
bottomButtonView.backgroundColor = .red
view.addSubview(bottomButtonView)
bottomButtonView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = false
bottomButtonView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
bottomButtonView.widthAnchor.constraint(equalToConstant: 400).isActive = true
bottomButtonView.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
bottomButtonView.heightAnchor.constraint(equalToConstant: 300).isActive = true
bottomButtonView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 400).isActive = true
Thank you in advanced.

Related

Translating/Scaling UIImageViews based on Screen Size

I have a blank rectangular uiview that dynamically resizes based on the device screen size.
Into the uiview, I programmatically insert uiimageview subviews that I then save CGRect, CGPoint, and CGAffineTransform data to my database.
I can then fetch this data and re-upload and view the uiimageviews I had inserted previously.
All is well if I have the same device screen size, but my issue is trying to adapt these saved data points to other screen sizes so it optically looks good no matter what iOS device you may be on.
I tried storing alongside the other data points, the original height and width of the screen so I can, for example, find the change in width from the original to adjust the original x-coordinate to move more or less points.
let widthPointChange = new.bounds.width - saved.originalSuperviewWidth
let heightPointChange = new.bounds.height - saved.originalSuperviewHeight
newUIImageView.center = CGPoint(x: saved.x + widthPointChange, y: saved.y + heightPointChange)
Is there a better way to do or think about this in terms translating/scaling according to various screen sizes but that stays true to the original? I'm wondering how, for example, Instagram is able to save user generated stories and display them consistently and proportionately across device sizes..
Try this approach!
let view = UIView()
let image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
view.addSubview(image)

Menu bar and images/buttons don´t align properly

I´m having trouble with NSLayoutConstraints. I wanted to create a custom menu bar right underneath the header with some images(or buttons) for the navigation but I have no idea why the images do not properly align in the middle and I guess the menu bar does not align underneath the header.. I tried to fix it with changing some values but I´m clueless at this point.
private func setUpMenuBar() {
view.addSubview(menuBar)
let viewWidth = view.viewWidth
menuBar.translatesAutoresizingMaskIntoConstraints = false
menuBar.centerXAnchor.constraint(equalTo:
view.centerXAnchor).isActive = true
menuBar.centerYAnchor.constraint(equalTo:
view.topAnchor).isActive = true
menuBar.heightAnchor.constraint(equalToConstant: 100).isActive
= true
menuBar.widthAnchor.constraint(equalToConstant:
viewWidth).isActive = true
}
Your menuBar is currently the same width as your view so this is why your buttons are evenly spaced across the view. Try making your menuBar half of the view width so that your buttons are closer together (you can experiment here):
menuBar.widthAnchor.constraint(equalToConstant: viewWidth / 2).isActive = true
If you also want a bit of space between the bar, try adding a topConstraint (you can experiment with changing the 30):
menuBar.topAnchor.constraint(equalToConstant: 30).isActive = true

View position glitches around in scroll view when scrolling

Whenever you scroll, the title view bugs out and rapidly switches between two positions. The label manages to stay within the constraints of the view, and it is just the view's position that bugs.
I've made a gif of the issue if it is still unclear or you need to see an example of the problem.
https://gph.is/g/EJ09l9Z
I've tried playing around with constraints to no avail. I still believe that the issue is to do with constraints, although I am not an expert by any means in Xcode or swift so I could be wildly wrong.
These are the constraints that I have been working with and around in attempts to get this working.
titleView.leftAnchor.constraint(equalTo: scrollView.contentView.leftAnchor).isActive = true
titleView.topAnchor.constraint(equalTo: scrollView.contentView.topAnchor, constant: 6).isActive = true
titleView.rightAnchor.constraint(equalTo: scrollView.contentView.rightAnchor, constant: 6).isActive = true
titleView.bottomAnchor.constraint(equalTo: timeView.topAnchor).isActive = true
titleLabel.leftAnchor.constraint(equalTo: titleView.leftAnchor, constant: 10).isActive = true
titleLabel.topAnchor.constraint(equalTo: titleView.topAnchor).isActive = true
titleLabel.rightAnchor.constraint(equalTo: titleView.rightAnchor).isActive = true
titleLabel.bottomAnchor.constraint(equalTo: timeView.topAnchor).isActive = true
titleLabel.layer.masksToBounds = true
The goal is for the title to stay in the one position as the view is scrolled, like the labels and views beneath it. Feel free to suggest any other fixes or ask for more code, I just don't know what the problem would be if not constraints.
Also for clarification, I am not using a UIScrollView, but rather an OLEContainerScrollView.
https://github.com/ole/OLEContainerScrollView
https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/

How to show MapKit compass?

Currently the compass only get's shown if a User applies a rotate-gesture. Otherwise the compass is hidden.
But it would be nice if my two wishes below were fulfilled!
Is it possible to display the compass always?
How to show / hide the compass-view using Swift?
You can do this quite easily in iOS 11 by using the new MKCompassButton class.
You need to create an instance of MKCompassButton and add it to your map view. You can then set its compassVisibility property to one of:
.visible - always visible
.never - never visible
.adaptive - the compasss is only visible if the map is rotated away from a North/up orientation.
If you keep a reference to the compass in a property you can change its visibility as you need:
mapview.showsCompass = false // Hide built-in compass
compassButton = MKCompassButton(mapView: mapview) // Make a new compass
compassButton.compassVisibility = .visible // Make it visible
mapview.addSubview(compassButton) // Add it to the view
// Position it as required
compassButton.translatesAutoresizingMaskIntoConstraints = false
compassButton.trailingAnchor.constraint(equalTo: mapview.trailingAnchor, constant: -12).isActive = true
compassButton.topAnchor.constraint(equalTo: mapview.topAnchor, constant: 12).isActive = true
Unfortunately, for prior versions of iOS there is no simple solution. I have seen suggestions that involve looking through the map view's subviews to try and find the compass view but there seems to be mixed results.

TopAnchor of viewController changin in iMessage Extension between presentation modes

I'm making a survey iMessage app (yeah I know) and having a problem moving between the presentation modes. The series of screenshots below shows that when the app launches, all is fine in the compact mode. When expanded everything is still correct but then when I go back to compact the content is shifted down by what looks like the same height as the large messages nav bar (86 I believe)
I've tried setting the top constraint to be -86 when switching back to the compact view, however, this either does nothing or sends it back to where is should be and then subtracts 86 so it dissapears too high up. I've based this project on the IceCream example project from app so not sure where this issue is coming from (probably autolayout but everything is pinned to the layout guides)
Here's the code that adds the view controller:
func loadTheViewController(controller: UIViewController) {
// Remove any existing child controllers.
for child in childViewControllers {
child.willMove(toParentViewController: nil)
child.view.removeFromSuperview()
child.removeFromParentViewController()
}
// Embed the new controller.
addChildViewController(controller)
controller.view.frame = view.bounds
controller.view.translatesAutoresizingMaskIntoConstraints = true
view.addSubview(controller.view)
controller.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
controller.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
controller.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
controller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
controller.didMove(toParentViewController: self)
}
I've been working on this for what feels forever so any suggestions welcome.
You are setting up constraints on view but you have set translatesAutoresizingMaskIntoConstraints to true. The autoresizing mask constraints will likely conflict with the constraints you are adding, cause unexpected results. You should change to:
controller.view.translatesAutoresizingMaskIntoConstraints = false
Also rather than pinning to view.topAnchor, you should pin to the topLayoutGuide, which will take the top navigation bar into account.
controller.view.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
Similarly,
controller.view.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true

Resources