Cant resize a UIButton with CGRectMake - ios

I have a button with a image on it, I am using UIView.animateWithDuration to assign a new size to the view as well as the image view.
The view scales up correctly, but there is no change to the image view
Code Snippet:
#IBAction func imageButtonDidTouch(sender: AnyObject) {
UIView.animateWithDuration(0.7) {
/*** Executes Properly ***/
self.dialogView.frame = CGRectMake(0, 0, self.screenWidth!, self.screenHeight!)
/*** Does Not Execute ****/
self.imageButton.frame = CGRectMake(0, 0, self.screenWidth!, 240)
/*** Executes Properly ***/
self.likeButton.hidden = true
self.shareButton.hidden = true
self.userButton.hidden = true
self.headerView.hidden = true
self.dialogView.layer.cornerRadius = 0
}
}
Screenshot Before Button Press:
Screenshot After Button Press:
Screen Width and Screen Height Have been Defined in viewDidLoad()
screenWidth = UIScreen.mainScreen().bounds.width
screenHeight = UIScreen.mainScreen().bounds.height
EDIT 1:
imageButton declaration
#IBOutlet weak var imageButton: UIButton!
EDIT 2:
I added a completion handler to the animate function:
#IBAction func imageButtonDidTouch(sender: AnyObject) {
print("Height Before:", self.imageButton.frame.height)
print("Width Before:",self.imageButton.frame.width)
UIView.animateWithDuration(0.7, animations: {
/*** Executes Properly ***/
self.dialogView.frame = CGRectMake(0, 0, self.screenWidth!, self.screenHeight!)
/*** Does Not Execute ****/
self.imageButton.frame = CGRectMake(0, 0, self.screenWidth!, 240)
/*** Executes Properly ***/
self.likeButton.hidden = true
self.shareButton.hidden = true
self.userButton.hidden = true
self.headerView.hidden = true
self.dialogView.layer.cornerRadius = 0
}) { (true) in
print("Height After:", self.imageButton.frame.height)
print("Width After:",self.imageButton.frame.width)
}
}
Console Screenshot:
View Hierarchy:

Check before setting imageButton size wether or not are you receiving you desired screenWidth.
The right place to get the UIScreen frame data is in viewDidLayoutSubviews as it is called after the subViews have been laid out in screen also it is called after every time your device changes orientation such as your width will be different when your user goes into landscape mode.This is called after viewWillAppear:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
screenWidth = UIScreen.mainScreen().bounds.width
screenHeight = UIScreen.mainScreen().bounds.height
}
Then:-
If you are initialising and declaring your button Programatically :
print(self.screenWidth)
self.imageButton.frame.size = CGSizeMake(self.screenWidth,240)
If you are using AutoLayout for the constraints, then :-
Create an #IBOutlet of your button's width constraint in your class
#IBOutlet weak var widthConstraint: NSLayoutConstraint!
Then set it accordingly.
widthConstraint = self.screenWidth
Note:
For conditions not using Auto Layout, when views are embedded within a view the CGRectMake function will only be executed for the parent view and not for the child view.
If needed to perform the operation with CGRectMake, Auto Layout needs to be disabled

Related

UIScrollView with glitch content on iPhone 11 Pro Max

I’m new in Swift and I tried making UIScrollView that shows view controllers.
Every thing perfect just at iPhone 11 Pro Max the next screen show a little bit on the side:
the orange strip is the next screen
My Code:
//MARK: - outlets
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var scrollView: UIScrollView!
//MARK: - properties
var viewControllers: [String] = ["ComputerViewController", "AttactViewController", "DefenceViewController", "OfflineViewController"]
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
//MARK: - life cyrcles
override func viewDidLoad() {
super.viewDidLoad()
for index in 0..<viewControllers.count {
frame.origin.x = scrollView.frame.size.width * CGFloat(index)
frame.size = scrollView.frame.size
let view = UIView(frame: frame)
let storyboard = UIStoryboard(name: "Menu", bundle: nil)
var controller: UIViewController = storyboard.instantiateViewController(withIdentifier: viewControllers[index]) as UIViewController
view.addSubview(controller.view)
self.scrollView.addSubview(view)
}
scrollView.contentSize = CGSize(width: scrollView.frame.size.width * CGFloat(viewControllers.count), height: scrollView.frame.size.height)
scrollView.delegate = self
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
var pageNumber = scrollView.contentOffset.x / scrollView.frame.size.width
pageControl.currentPage = Int(pageNumber)
}
thanks for helping...
A couple of observations:
You should avoid referencing frame in viewDidLoad. At this point, the frame is not known.
You should avoid hard-coding the size and placement of the subviews at all. There can be a variety of events that change the view’s size later on (e.g. rotations, split view multitasking, etc.). Use constraints.
With scroll views, there are two layout guides, one for its frame and another for its contentSize. So set the size of the subviews using the frameLayoutGuide and the placement of these subviews relative to the contentLayoutGuide.
When you add a view controller’s view as a subview, make sure you call addChild(_:) and didMove(toParent:) calls for view controller containment. See “Implementing a Container View Controller” section of the view controller documentation.
If you want to add paging behavior, just set isPagingEnabled of the scroll view.
Pulling that all together:
override func viewDidLoad() {
super.viewDidLoad()
addChildViews()
}
override func viewDidLoad() {
super.viewDidLoad()
var anchor = scrollView.contentLayoutGuide.leadingAnchor
for identifier in viewControllers {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let child = storyboard.instantiateViewController(withIdentifier: identifier)
addChild(child) // containment call
child.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(child.view)
child.didMove(toParent: self) // containment call
NSLayoutConstraint.activate([
// define size of child view (relative to `frameLayoutGuide`)
child.view.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
child.view.heightAnchor.constraint(equalTo: scrollView.frameLayoutGuide.heightAnchor),
// define placement of child view (relative to `contentLayoutGuide`)
child.view.leadingAnchor.constraint(equalTo: anchor),
child.view.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
child.view.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
])
anchor = child.view.trailingAnchor
}
anchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
scrollView.isPagingEnabled = true
}
I’ve eliminated the property frame as that’s not needed anymore (and is just a source of confusion with the view controller’s view’s property of the same name). I’ve also eliminated the container view as it didn’t add much to the overall solution (and only adds a layer of constraints to add).
But the key is to use constraints to dictate the size and position of the subviews within the scroll view and to use view controller containment API.

UIView - What override will allow updating a UIView frame

I have a UIView .xib, and am subclassing UIView. When I load the Nib, the frame of the view is set and awakeFromNib() is called.
I have 3 buttons. When I load the view, I pass in callbacks for each button. If one or more of the callbacks is nil, then I hide the buttons and resize the view:
let viewSize = 50
var adjustedSize = 0
if (self.option2String == nil){
self.option2View.isHidden = true
adjustedSize -= viewSize
}
if (self.option3String == nil){
self.option3View.isHidden = true
adjustedSize -= viewSize
}
let _size = self.innerView.frame.size
let size = CGSize(width: _size.width, height: _size.height + CGFloat(adjustedSize))
self.innerView.frame = CGRect(origin: self.innerView.frame.origin, size: size)
I have tried putting this code in awakeFromNib(), and didMoveToSuperview(), but the frame does not change size.
If I enclose the last line in DispatchQueue.main.async, then it works. But I'm concerned that this is just luck due to timing.
Is this best practice? Where can I resize a view from within a UIView subclass?
EDIT: Confirmed, the DispatchQueue.main.async is just luck. It only works 50% of the time.
Having the view inside a UIViewController, call a function that resizes your view inside the controller's viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
myView.resize()
}

Setting origin of container to another container trouble (Swift 4.2)

I'm trying to set the origin and width/height of one UIView (red) to a second UIView (blue).
I am calling UIView.frame.origin or size and for some reason the y origin doesn't work.
I've also tried with layout constraints (see it commented out below), but this is overriding my blue fully constrained view.
Then I have a button that animates the red view to the side so you can see the blue view underneath, but I can't get them to line up to start with. Below is my code. In interface builder, I have both UIViews set up as containers. Blue is fully constrained with auto layout and red has no constraints.
import UIKit
class ViewController: UIViewController
{
#IBOutlet weak var blueContainer: UIView!
#IBOutlet weak var redContainer: UIView!
#IBOutlet weak var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
print(redContainer.frame)
redContainer.frame.origin.x = view.frame.width/2
redContainer.frame.size.width = view.frame.width
//try to line up y with origin and size
redContainer.frame.origin.y = blueContainer.frame.origin.y
redContainer.frame.size.height = blueContainer.frame.size.height
//also tried by using constraints
//redContainer.topAnchor.constraint(equalTo: blueContainer.topAnchor).isActive = true
//redContainer.heightAnchor.constraint(equalTo: blueContainer.heightAnchor).isActive = true
print(redContainer.frame)
}
#IBAction func slideRed(_ sender: Any) {
if redContainer.frame.origin.x == 0 {
UIView.animate(withDuration: 0.5) {
self.redContainer.frame.origin.x = self.view.frame.width/2
}
button.setTitle("Come Back Red!", for: .normal)
} else {
UIView.animate(withDuration: 0.5) {
self.redContainer.frame.origin.x = 0
}
button.setTitle("Go Away Red!", for: .normal)
}
}
}
ViewDidLoad does not guarantee the view has laid out its constraints. So when blueContainer's frame and size is zero, you will not see any effect on redContainer. You should use viewDidLayoutSubviews to get the correct frame and size from blueContainer.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
redContainer.frame.origin.x = view.frame.width/2
redContainer.frame.size.width = view.frame.width
//try to line up y with origin and size
redContainer.frame.origin.y = blueContainer.frame.origin.y
redContainer.frame.size.height = blueContainer.frame.size.height
}

Update constraints swift layoutIfNeeded doesn't work

I need to update constraints (height of my CollectionView) when request is done and I have images from server, so height of View also will change.
Result screen
what I need screen
My code:
DispatchQueue.main.async {
self.cvActivity.alpha = 0
self.collectionView.reloadData()
self.collectionView.heightAnchor.constraint(equalToConstant: self.cellWidth * 2).isActive = true
self.collectionView.setNeedsUpdateConstraints()
self.collectionView.setNeedsLayout()
self.view.layoutIfNeeded()
}
Well, basic idea by #J. Doe is correct, here some code explanation (for simplicity i used UIView, not UICollectionView):
import UIKit
class ViewController: UIViewController {
#IBOutlet var heightConstraint: NSLayoutConstraint! // link the height constraint to your collectionView
private var height: CGFloat = 100 // you should keep constraint height for different view states somewhere
override func updateViewConstraints() {
heightConstraint.constant = height // set the correct height
super.updateViewConstraints()
}
// update height by button press with animation
#IBAction func increaseHight(_ sender: UIButton) {
height *= 2
UIView.animate(withDuration: 0.3) {
self.view.setNeedsUpdateConstraints()
self.view.layoutIfNeeded() // if you call `layoutIfNeeded` on your collectionView, animation doesn't work
}
}
}
Result:
Define a height for your collectionView, create an outlet from that constraint and increase the constant of that constraint and call layoutifneeded in an animation block
You need to make object of your Height constraint from storyboard
#IBOutlet weak var YourHeightConstraintName: NSLayoutConstraint!
YourConstraintName.constant = valueYouWantToGive
---------OR--------
collectionViewOutlet.view.frame = CGRect(x:
collectionViewOutlet.frame.origin.x , y:
collectionViewOutlet.frame.origin.y, width:
collectionViewOutlet.frame.width, height:
yourheightValue)

UIScrollView containing CGRect line graph not scrolling (Swift)

I'm trying to make a line graph drawn using a CGRect scrollable by the user. I've set its container view to be scrollable (i want a horizontal scroll so the line can extend past the screen), but it doesn't appear to be having any effect. Here's the code in my actual view controller:
#IBOutlet weak var containerView: UIScrollView!
#IBOutlet weak var graphView: GraphView!
override func viewDidLoad() {
super.viewDidLoad()
logWeightButton.enabled = false
logWeightField.delegate = self
logWeightField.keyboardType = .NumberPad
self.view.addSubview(containerView)
containerView.addSubview(graphView)
containerView.contentSize = graphView.rectDisplay.size
pageLoad()
}
and here's the code declaring the CGRect(within a UIView class)
override func drawRect(rect: CGRect) {
if dataPoints.count == 0 {
return
} else {
self.drawLine(dataPoints, rect: rect, xCoordinates: nil, color: UIColor.blackColor())
if xCoordinates.count != 0 {
self.drawLine(actualPoints, rect: rect, xCoordinates: xCoordinates, color: UIColor.redColor())
}
rectDisplay = rect
}
}
Figured out that I needed to turn off auto-layout in the storyboard file inspector - if it's not disabled, it can overwrite scroll views.
I also made sure to enable scroll with
containerView.scrollEnabled = true
and set the containerView frame to
self.view.frame
I don't have enough reputation to post an image, so here it is:
http://i.stack.imgur.com/ZOBJp.png
The graph now scrolls, but my whole display is a mess (I believe because of setting the container frame to the size of the view), so the cycle continues.
FINAL UPDATE:
Learned that the frame of the scroll view has to be smaller than its content for it to scroll (which is why setting it to the view frame worked - the width of the view was less than the width of the line graph), so I set my frame to
containerView.frame = CGRect(x: 43.0, y: 147.0, width: 300.0, height: 117.0)
And everything works now. Hope this is helpful for someone else!

Resources