UIView - What override will allow updating a UIView frame - ios

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()
}

Related

How do I prevent Keyboard from adding its own height constraint when using view as inputAccessoryView

I'm trying to use a custom view as an accessory view over the keyboard, for various reasons, in this case, it is much preferred over manual keyboard aligning because of some other features.
Unfortunately, this is a dynamic view that defines its own height. The constraints all work fine outside of the context of an accessoryView without errors, and properly resizing
When added as a keyboardAccessoryView it seems to impose a height of whatever the frame is at the time and break other height constraints
It appears as:
"<NSLayoutConstraint:0x600003e682d0 '_UIKBAutolayoutHeightConstraint' Turntable.ChatInput:0x7fb629c15050.height == 0 (active)>"
(where 0 would correspond to whatever height had been used at initialization
It is also labeled accessoryHeight which should make it easy to remove, but unfortunately, before I can do this, I'm getting unsatisfiable constraints and the system is tossing my height constraints
Tried:
in the inputAccessoryView override, I tried to check for the constraints and remove it, but it doesn't exist at this time
setting translatesAutoresizing...Constraints = false
tl;dr
Using a view as a KeyboardAccessoryView is adding its own height constraint after the fact, can I remove this?
Looks like keyboard doesn't like inputAccessoryView with height constraint. However you still can have inputAccessoryView with dynamic height by using frame (it is still possible to use constraints inside your custom inputAccessoryView).
Please check this example:
import UIKit
final class ViewController: UIViewController {
private let textField: UITextField = {
let view = UITextField()
view.frame = .init(x: 100, y: 100, width: 200, height: 40)
view.borderStyle = .line
return view
}()
private let customView: UIView = {
let view = UIView()
view.backgroundColor = .red
view.frame.size.height = 100
view.autoresizingMask = .flexibleHeight // without this line height won't change
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(textField)
textField.inputAccessoryView = customView
textField.becomeFirstResponder()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.customView.frame.size.height = 50
self.textField.reloadInputViews()
}
}
}

How to dynamically resize xib according to UITextView

I am using auto layout.
I am loading UIView from xib.
All elements have static size except UITextView.
I've disabled scroll on UITextView
UITextView is filled with the text of different size on ViewDidLoad(). So it can be 1 line or 7 lines there for example.
I am loading this UIView with the following method:
fileprivate func setTableHeaderView() {
guard let _headerView = UINib(nibName: "TableHeaderView",
bundle: nil).instantiate(withOwner: self, options: nil)[0] as? UIView else { return }
tableView.tableHeaderView = UIView(frame:
CGRect(x: 0, y: 0, width: tableView.frame.width, height: _headerView.frame.size.height))
configureViewController() // here I set the text for UITextView
tableView.tableHeaderView?.addSubview(_headerView)
}
When I load this UIView the height is always the same both before and after layoutSubviews. Actually the size always is equal to the initial size of the xib that is set in the Size Inspector of XCode.
I want to UITextView to fit the text size and to have different xib UIView size that will depend on the size of UITextView + all other elements + constraints.
I've tried different ways to implement this but but none did not help. What I've tried:
to set constraints in different ways
to call layoutSubviews() forcibly and check the size of the resulted UIView
to check size of UIView after viewDidLayoutSubviews
translatesAutoresizingMaskIntoConstraints = true with autoresizingMask = .flexibleHeight
Is it possible to implement this idea in such way?
Try to override viewDidLayoutSubviewsMethod
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Dynamic sizing for the header view
if let headerView = tableView.tableHeaderView {
let height = headerView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
var headerFrame = headerView.frame
// If we don't have this check, viewDidLayoutSubviews() will get
// repeatedly, causing the app to hang.
if height != headerFrame.size.height {
headerFrame.size.height = height
headerView.frame = headerFrame
tableView.tableHeaderView = headerView
}
}
}

Cant resize a UIButton with CGRectMake

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

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!

Swift: UITableView increments layoutSubviews() when scrolling

I have a table view with custom cell in Swift that contains a horizontal layout UIView. I have noticed that when scrolling the correctly positioned subview in the horizontal view starts to multiply. I guess this has something to do with layoutSubviews() being called when table scrolls and the fact tableview recycles its cells when hidden and shows them when needed, but ignores currently positioned subviews..
It looks something like this
There's already a similar question from before, but it has no good answer.
UIScrollview calling superviews layoutSubviews when scrolling?
Here's the code I'm using inside my custom cell for horizontal positioning:
class HorizontalLayout: UIView {
var xOffsets: [CGFloat] = []
override func layoutSubviews() {
var width: CGFloat = 0
for i in 0..<subviews.count {
var view = subviews[i] as UIView
view.layoutSubviews()
width += xOffsets[i]
view.frame.origin.x = width
width += view.frame.width
}
self.frame.size.width = width
}
override func addSubview(view: UIView) {
xOffsets.append(view.frame.origin.x)
super.addSubview(view)
}
func removeAll() {
for view in subviews {
view.removeFromSuperview()
}
xOffsets.removeAll(keepCapacity: false)
}
}
Taken from here: https://medium.com/swift-programming/dynamic-layouts-in-swift-b56cf8049b08
Using inside custom cell like so:
func loadStops(stops:[String]) {
for stop in stops {
// just testing purposes only
let view = UIView(frame: CGRectMake(10, 0, 40, 40))
view.backgroundColor = UIColor.redColor()
stopsView.addSubview(view)
}
}
Is there a way to fix this problem and prevent the subview of being multiplied when scrolling and perhaps a better way to position subviews horizontally in a tableview cell?

Resources