How to change the UIAlertController's width in Swift 4? - swift

Here is one way to change the AlertController's width:
let alertController = UIAlertController(title: "Disconnected", message: "", preferredStyle: .alert)
let width : NSLayoutConstraint = NSLayoutConstraint(item: alertController.view, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 160)
alertController.view.addConstraint(width)
But there will be an error message when running:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x60000009ae50 UIView:0x7f9c4473cbf0.width == 270 (active)>",
"<NSLayoutConstraint:0x6040002824e0 _UIAlertControllerView:0x7f9c460d4e00'Disconnected'.width >= UIView:0x7f9c4473cbf0.width (active)>",
"<NSLayoutConstraint:0x60c00008be00 _UIAlertControllerView:0x7f9c460d4e00'Disconnected'.width == 160 (active)>"
)
Is there any other way to change the AlertController's width? Or is there any way to fix the error?
Any suggestions would be appreciated.

Related

How can I set priority on constraints in Swift?

I've been struggling to make priority on constraints work programmatically in Swift.
My goal is to have the meetingFormView no more than 300 wide. Using IB I would give the width constraint a lower priority and give a higher priority to the "lessThanOrEqualToConstant". But I can't get it to work.
I've tried this:
meetingFormView.translatesAutoresizingMaskIntoConstraints = false
let constraintWidth = NSLayoutConstraint(
item: meetingFormView,
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: startView,
attribute: NSLayoutAttribute.width,
multiplier: 1,
constant: 0)
constraintWidth.priority = .defaultHigh
NSLayoutConstraint.activate([
meetingFormView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
meetingFormView.heightAnchor.constraint(equalToConstant: 170),
meetingFormView.widthAnchor.constraint(lessThanOrEqualToConstant: 300),
meetingFormView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
constraintWidth
])
It actually seems to take three lines of code to set up a prioritized "anchor-based" constraint in code:
let widthConstraint = meetingFormView.widthAnchor.constraint(equalToConstant: 170)
widthConstraint.priority = UILayoutPriority(rawValue: 500)
widthConstraint.isActive = true
It seems like if I try to set isActive with the let declare Xcode (maybe Swift?) doesn't recognize that the type is NSLayoutConstraint. And using UILayoutPriority(rawValue:) seems to be the best (only?) way to set priorities.
While this answer doesn't conform exactly with what you are doing, I believe it will work with IB. Just replace the let with creating an IBOutlet and there should be no need for the isActive line.
Obviously, to change the priority later in code you just need:
widthConstraint.priority = UILayoutPriority(rawValue: 750)
You can write a small extension like this:
extension NSLayoutConstraint
{
func withPriority(_ priority: Float) -> NSLayoutConstraint
{
self.priority = UILayoutPriority(priority)
return self
}
}
then use it like this:
NSLayoutConstraint.activate([
...
meetingFormView.widthAnchor.constraint(lessThanOrEqualToConstant: 300).withPriority(500),
...
])

How to fix UIImageView Aspect Fit after download

I'm setting the image view to aspect-fit and resizing it after the image is downloaded, to remove the extra padding on top and bottom. What I have works fine unless the first image doesn't fill the whole screen, it will crop the second image incorrectly. If I back out of the screen and go back in it's fixed. Any idea how to fix this? I've added an estimated row height as well.
Inside my cell:
let mainImageUrl = URL(string: noteImage!.imageUrl)
self.noteImageView.kf.indicatorType = .activity
self.noteImageView.kf.setImage(with: mainImageUrl, completionHandler: {
(image, error, cacheType, imageUrl) in
if image != nil {
let constraint = NSLayoutConstraint(
item: self.noteImageView, attribute: NSLayoutAttribute.height,
relatedBy: NSLayoutRelation.equal,
toItem: self.contentView, attribute: NSLayoutAttribute.width,
multiplier: (image!.size.height - 30) / (image!.size.width), constant: 0.0)
self.contentView.addConstraint(constraint)
} else {
print("error downloading image")
}
})
Here is what it looks like, scenario 1 with a portrait tall image works fine. Scenario 2 with a short image crops it weird.

Can't add constraints to video programmatically

I am trying to programmatically constraint a video into the center of the page. My AV controller is called avPlayerController .
I have already given its x and y values along with the width and height:
avPlayerController.view.frame = CGRect(x: 36 , y: 20, width: 343, height: 264)
So how do i center it?
I HAVE TRIED: Programmatically Add CenterX/CenterY Constraints
But, as you can guess it did not work :(
Here is my code:
super.viewDidLoad()
let filepath: String? = Bundle.main.path(forResource: "rockline", ofType: "mp4")
let fileURL = URL.init(fileURLWithPath: filepath!)
avPlayer = AVPlayer(url: fileURL)
let avPlayerController = AVPlayerViewController()
avPlayerController.player = avPlayer
avPlayerController.view.frame = CGRect(x: 36 , y: 20, width: 343, height: 264)
// hide/show control
avPlayerController.showsPlaybackControls = false
// play video
avPlayerController.player?.play()
self.view.addSubview(avPlayerController.view)
avPlayerController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
Here's the code, with explanation.
Always remember that if you are using auto layout constraints, do not set frames. The layout engine will walk all over them. If you are instantiating your view in code, don't set a frame, or if necessary, it communicates things best if you set the frame to CGRect.zero.
Understand the view life cycle. Specifically, you can set your constraints in viewDidLoad, where they should be created only once.
Remember to set the auto resizing mask to false. This is the most common error when you learning auto layout in code.
There are actually three ways to create constraints, and a few ways to activate them. In your question, I think the easiest way is to use anchors.
Here's an example of centering a view (any view) with a width of 343 and a height of 264:
let myView = UIView() // note, I'm not setting any frame
override func viewDidLoad() {
super.viewDidLoad()
view.translatesAutoresizingMaskIntoConstraints = false
myView.translatesAutoresizingMaskIntoConstraints = false
myView.widthAnchor.constraint(equalToConstant: 343.0).isActive = true
myView.heightAnchor.constraint(equalToConstant: 264.0).isActive = true
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
That's all there is to it! BUT....
I'd suggest one more thing. Don't use constants in setting the height and width. That's not being "adaptive". Your 4 inch iPhone SE has a screen size of 568x320, where this may look centered and large enough. But on an iPhone Plus with a screen size of 736x414 it may be pretty small. (To say nothing of a 12.9 inch iPad Pro!)
Notice how my code uses the superview for the centerX/centerY anchors. (And instead of equalToConstant it's equalTo.) Do the same with the width and height. Through the use of multiplier and constant, along with UILayoutGuides, you can make your layouts adapt to whatever screen size Apple throws at you.
You can try this.
avPlayerController.view.enableAutoLayoutConstraint()
avPlayerController.view.setCenterXConstraint(.equal, constantValue: 0)
avPlayerController.view.setCenterYConstraint(.equal, constantValue: 0)
extension UIView
{
//Method to making translatesAutoresizingMaskIntoConstraints false.
func enableAutoLayoutConstraint()
{
self.translatesAutoresizingMaskIntoConstraints = false
}
//Method to set Center-X Consttraint
func setCenterXConstraint(_ relationType:NSLayoutRelation , constantValue:CGFloat)->NSLayoutConstraint
{
let constraint = NSLayoutConstraint(item: self, attribute:.centerX, relatedBy: relationType, toItem: self.superview, attribute: .centerX, multiplier: 1, constant: constantValue)
self.superview?.addConstraint(constraint)
return constraint
}
//Method to set Center-Y Consttraint
func setCenterYConstraint(_ relationType:NSLayoutRelation , constantValue:CGFloat)->NSLayoutConstraint
{
let constraint = NSLayoutConstraint(item: self, attribute:.centerY, relatedBy: relationType, toItem: self.superview, attribute: .centerY, multiplier: 1, constant: constantValue)
self.superview?.addConstraint(constraint)
return constraint
}
}

Customize UIView as background

I would like to use UIView to create the dark gray curvy as a design.
However, I think I am doing it wrong when I try to set the cornerRadius and I know cornerRadius is only for the corner. Maybe I am not aware that there is actually another way to go around and would like to know if there an easy way to go around. The way I am doing this is, I created an UIView on StoryBoard and I create a Cocoa Touch Class that is Subclass of UIView and have configure the Custom Class to inherit the traits.
Any suggestion is really appreciated here.
That curve is not something you will be able to achieve using cornerRadius. There are two easy ways you can have what you want: A pre-rendered UIImage or a custom drawn CAShapeLayer.
Option 1: UIImage Background
You can create a background image in your favorite image editor, then set it as a centered background:
// create the UIImageView to display the image
let backgroundImageView = UIImageView()
backgroundImageView.image = UIImage(named: "background")
backgroundImageView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(backgroundImageView)
// create constraints to position the UIImageView and apply them
let horizontalConstraint = NSLayoutConstraint(item: backgroundImageView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: backgroundImageView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
view.addConstraints([horizontalConstraint, verticalConstraint])
Put that in the viewDidLoad() of the UIViewController you want to have a background.
You'll need to create your background image sized for the largest screen you're targeting and add it to your app's assets. You can experiment with setting constraints on the size to get it just the way you want it on every screen.
The advantage of this method is that if you want your background to be something more elaborate, you can just edit the image.
Option 2: CAShapeLayer Background
For your specific case (a grey curve) you can do it pretty easily with vectors:
// decide on size of ellipse and how to center it
let ellipseWidth:CGFloat = view.frame.width * 7
let ellipseHeight:CGFloat = view.frame.height * 3
let ellipseTop:CGFloat = 100
let ellipseLeft:CGFloat = -ellipseWidth / 2 + view.frame.width / 2
// create the ellipse path
let ellipseOrigin = CGPoint(x:ellipseLeft,y:ellipseTop)
let ellipseSize = CGSize(width: ellipseWidth, height: ellipseHeight)
let ellipsePath = CGPath(ellipseIn: CGRect(origin: ellipseOrigin, size: ellipseSize), transform: nil)
// create the shape layer
let shapeLayer = CAShapeLayer()
shapeLayer.frame = view.frame
shapeLayer.fillColor = UIColor.darkGray.cgColor
shapeLayer.path = ellipsePath
view.layer.addSublayer(shapeLayer)
You might have to experiment with the proportions of the ellipse to get it to look just right. 7x3 seems to look pretty close to your example.
If you want to get fancier you can add other shapes, strokes, gradients, etc., or you can use a UIBezierPath to get exactly the right curve.
Also note for this to work you'll need
import CoreGraphics
at the top of your file.

My constraint breaking log doesn't make any sense, can someone help me understand?

So in my tableViewCell I have a imageView which has 0 points constraint to the trailing and leading superview. Top constraints of 8 and Bottom constraint of 30 to the superview. And then I add aspect constraint of the imageView according to the image with this code:
override var image: UIImage? {
didSet {
if let image = image {
let aspect = image.size.width / image.size.height
aspectConstraint = NSLayoutConstraint(item: self,
attribute: NSLayoutAttribute.width,
relatedBy: NSLayoutRelation.equal,
toItem: self,
attribute: NSLayoutAttribute.height,
multiplier: aspect,
constant: 0.0)
}
}
}
internal var aspectConstraint : NSLayoutConstraint? {
didSet {
if oldValue != nil {
self.removeConstraint(oldValue!)
}
if aspectConstraint != nil {
self.addConstraint(aspectConstraint!)
}
}
}
how is my code breaking any constraints? they have nothing to do with eachother
2017-01-10 21:15:18.502301 Gymkhana[4538:65958] [LayoutConstraints]
Unable to simultaneously satisfy constraints. Probably at least one
of the constraints in the following list is one you don't want. Try
this: (1) look at each constraint and try to figure out which you
don't expect; (2) find the code that added the unwanted constraint
or constraints and fix it. (
"<NSLayoutConstraint:0x78698d60 UILabel:0x7869e890'Identical twins play a mi...'.top == UITableViewCellContentView:0x7869e500.topMargin (active)>",
"<NSLayoutConstraint:0x7869c930 V:[UILabel:0x79ba65a0'1h']-(8)-[Gymkhana.ThumbnailImageView:0x7869c340] (active)>",
"<NSLayoutConstraint:0x786a9ef0 H:[Gymkhana.ThumbnailImageView:0x7869c340]-(0)-| (active, names: '|':UITableViewCellContentView:0x7869e500 )>",
"<NSLayoutConstraint:0x78689700 H:|-(0)-[Gymkhana.ThumbnailImageView:0x7869c340] (active, names: '|':UITableViewCellContentView:0x7869e500 )>",
"<NSLayoutConstraint:0x7869a1b0 V:[Gymkhana.ThumbnailImageView:0x7869c340]-(30)-| (active, names: '|':UITableViewCellContentView:0x7869e500 )>",
"<NSLayoutConstraint:0x7869ca10 V:[UILabel:0x7869e890'Identical twins play a mi...']-(8)-[UILabel:0x79ba65a0'1h'] (active)>",
"<NSLayoutConstraint:0x798fb620 Gymkhana.ThumbnailImageView:0x7869c340.width == Gymkhana.ThumbnailImageView:0x7869c340.height (active)>",
"<NSLayoutConstraint:0x79b85b80 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7869e500.height == 329 (active)>",
"<NSLayoutConstraint:0x79b82b80 'UIView-Encapsulated-Layout-Width' UITableViewCellContentView:0x7869e500.width == 320 (active)>" )
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x798fb620
Gymkhana.ThumbnailImageView:0x7869c340.width==Gymkhana.ThumbnailImageView:0x7869c340.height (active)>
Constraints can be treated rules that AutoLayout uses when trying to calculate 4 parameters of a view: x, y (meaning coordinates of the top left corner), width and height. You put 5 constraints on your image view so, except for special cases, there will be some conflicts between them.
The first 4 constrains you mentioned already give enough information for the AutoLayout to calculate all 4 parameters for your imageView that I mentioned. Then aspect ratio constraint comes in and that is almost certain to be in conflict with other constraints unless your previous already lead to exactly same aspect as you set in the constraint.
There are 2 ways you could solve this:
If the image you want to set to the imageView already has the aspect ratio you want: remove the aspect ratio constraint completely. Use imageView's contentMode property instead.
If you really do need a specific aspect ratio to your imageView, then you need to rethink your other 4 constraints, since it clearly seems that some of them do not represent accurately how you want your imageView to be positioned and resized. For example: perhaps you don't need the leading and trailing constraints and you just wanted the imageView to have same distance from both left and right side of its superview? Then remove trailing and leading constraints and add a "center horizontally" constraint instead.