Trailing Edge in Scrollview in Keyboard Nib - ios

I currently have a keyboard app extension with a scrollview and a variety of buttons inside the scrollview. The ScrollView is set inside a parent view. Constraints are set on the parent view to the main view top, bottom, trailing, and leading edges. The ScrollView is set to equal widths to its parent.
I'm having a major issue constraining the trailing edge of the right most button to the right hand side of the ScrollView. In devices who's width is 320 it aligns just fine, but in the larger devices the keyboard is not expanding to fill the width of the device.
I tried setting the width in viewWillLayoutSubviews but it does nothing to change the positioning of the button. Confirmed that the keyboard width = 375.
override func viewWillLayoutSubviews() {
var bounds = UIScreen.mainScreen().bounds
var width = bounds.size.width
keyboardView.frame.size.width = width
scrollView.frame.size.width = width
println("Keyboard Width: \(keyboardView.frame.size.width)")
println("Scroll View: \(scrollView.frame.size.width)")
}

Related

Scroll UIView to expand into iPhone X top notch

I have the following layout:
UIImageView
UIView (with UILabel)
UITableView
As the tableView is scrolled up, the height of the imageView is decreased before actually scrolling the tableView. The following code is used for that:
let headerImageViewMaxHeight: CGFloat = 200
let headerImageViewMinHeight: CGFloat = UIApplication.shared.statusBarFrame.height
#IBOutlet var headerImageViewHeightConstraint: NSLayoutConstraint!
#IBOutlet var headerImageView: UIImageView!
#IBOutlet var subtitleView: UIView!
#IBOutlet var subtitleLabel: TextLabel!
private var contentOffsetDictionary: NSMutableDictionary!
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.isKind(of: UICollectionView.self) {
let horizontalOffset: CGFloat = scrollView.contentOffset.x
let collectionView: UICollectionView = scrollView as! UICollectionView
contentOffsetDictionary.setValue(horizontalOffset, forKey: collectionView.tag.description)
} else if scrollView == tableView {
let y: CGFloat = scrollView.contentOffset.y
let newHeaderImageViewHeight: CGFloat = headerImageViewHeightConstraint.constant - y
if newHeaderImageViewHeight > headerImageViewMaxHeight {
headerImageViewHeightConstraint.constant = headerImageViewMaxHeight
} else if newHeaderImageViewHeight < headerImageViewMinHeight {
headerImageViewHeightConstraint.constant = headerImageViewMinHeight
} else {
headerImageViewHeightConstraint.constant = newHeaderImageViewHeight
scrollView.contentOffset.y = 0
}
}
}
The following two images shown the current states of scrolled down or up:
Scrolled down (initial state).
Scrolled up.
As you can see, initially the grey bar saying To Table -> Table 1 is scrolled down and is about 60 high.
When it's scrolled up, it scrolles until the safeArea. What I want to achieve is that once it reaches the safe area, it continues scrolling up into the safeArea (top notch), while keeping the text in exactly the same place (topSpace to safeArea of the text should stay the same). So, basically growing the UIView to go into the safeArea.
This image shows the ViewController layout.
Header View can be ignored, as that is aligned to the top of the viewController, but no constraints to anything else than superView.
TopSpace of the view in question, is 0 to the UIImageView.
Changing headerImageViewMinHeight to 0, makes it scroll up to the actual top of the screen, without expanding it. So this seems like a good start, but needs some extra logic or constraints.
As the UIView doesn't have a specific height constraint, changing its UILabel's top space from = 16 to => 16, there's the warning of missing Y constraint or height.
Otherwise that, together with UILabel's top space to the viewController's safeArea of => 8 sounds like it could work.
Any ideas are welcome.
EDIT: The following picture basically shows what I want, except that here the UILabel's top is not aligned to the safe area.
What this picture shows is achieved by changing headerImageViewMinHeight to 0.
Add constriant
Uncheck constraint to margin
double click on Align bottom/ Align top
click on last baseline (for bottom)/ first baseline (for top constrraint)
DONE

Horizontal scroll view scrolls but only partially and bounces back to original position

I've got a horizontal scroll view with content as follows:
When I run it scrolls horizontally partially - I can scroll until about half of the red view is visible but then it bounces back.
How can I get it so that it can scroll all the way so all the red view is visible and then stays there?
I have this in the view controller, but it makes no difference if there or not.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
In the screenshot of the storyboard there is no trailing edge constraint for the red view, however if I add one between the red view and the content view then when I run on the device it stops scrolling and looks like this:
You shouldn't need to be explicitly setting the .contentSize to begin with -- you can let auto-layout handle it all for you.
First, delete your Content View.width = width constraint:
Having that constraint told auto-layout to make your contentView only as wide as the scroll view, so you wouldn't get any horizontal scrolling. By explicitly setting the .contentSize you got some scrolling, but as you found it didn't give you what you wanted.
After deleting that constraint, add a 20-pt trailing constraint from Red View to the trailing edge of the content view:
Now, you have a complete chain of horizontal constraints...
- Blue View.leading = leading + 20
- Blue View Width
- Red View.leading = Blue View.trailing + 40
- Red View Width
- trailing = Red View.trailing + 20
This satisfies auto-layout and properly defines the width of Content View... and since Content View is constrained to leading and trailing of your Scroll View, you get correct horizontal scrolling.
No need for any code.
I got it to work by adding the following:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
scrollView.contentSize = CGSize(width: 750,height: 812)
}
But why does the above work but not the following?
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("Content view bounds: \(contentView.bounds)")
scrollView.layoutIfNeeded()
scrollView.contentSize = contentView.bounds.size
}
At runtime contentView.bounds is (0.0, 0.0, 750.0, 812.0)

Changing frame and contentInset of UICollectionView in layoutSubviews() using IGListKit

I need to change the frame and top content inset (contentInset.top) of my collection view (which is a UICollectionView). The top inset and frame changes depend on the bounds of the superview and the content offset of the collection view, thus I put the inset-changing code in layoutSubviews().
override func layoutSubviews() {
super.layoutSubviews()
collectionView.collectionViewLayout.invalidateLayout()
collectionView.contentInset.top = new_inset_top
collectionView.frame = new_frame
}
However, the collection view does not account for the new insets and the log shows the following:
The behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less than the height of the UICollectionView
minus the section insets top and bottom values, minus the content
insets top and bottom values.
How can I fix this to let the collection view displays correctly?
I am using IGListKit
You do not need to do all these inside of layoutSubviews.
in viewDidLoad implement it like following
func viewDidLoad() {
super.viewDidLoad()
// let say you want 40px top inset
collectionView.contentInset = UIEdgeInsetsMake(40, 0, 0, 0)
}
Another thing is that, i don't get, why are you setting the frame in layoutSubviews. There is no need to set frame to change the contentInset.

Set Height of UIView on the basis of the content inside

I am adding an OverlayView to a Controller. I have set all the constraints to my OverlayView except the bottom constraint. I have a button at the end of my view. I am setting the size of the OverlayView as button.frame.maxY + margin. But it's not setting up the right height.
override func layoutSubviews() {
sizeToFit()
var sizeThatFits: CGSize = bounds.size
sizeThatFits.width = UIScreen.main.bounds.size.width - popupSizeHorizontalMargin
sizeThatFits.height = getStartedButton.frame.maxY + popupBottomMargin
let newFrame = CGRect(x: popupOriginX, y: bounds.midY - sizeThatFits.height/2, width: sizeThatFits.width, height: sizeThatFits.height)
frame = newFrame
}
This can be accomplished according to this [inner elements are example]
OverlayView
-> label ------> top to overlayView ,left,right
-> button -----> top to label , left , right
-> label -----> top to button , left , right , bottom to overlayView
The overlay view or outer view can calculate its height based on the size of elements inside it and their intrinsic size. Instead of specifying height constraint for overlay view or bottom constraint, add bottom constraint of overlay view to the last element inside the overlay view. This will satisfy the constraints and your overlay view will expand based on its contents.
Simply ctrl drag from your overlay view to the last inner view and select Bottom Space to Container.
You need to override intrinsicContentSize in UIView and return your custom size:
class YourCustomView: UIView {
override var intrinsicContentSize: CGSize {
var someYourSize = CGSize()
// ... calculate your size
return someYourSize
}
}
It will automatically update YourCustomView size to your custom calculated size using autolayout or when you want to update size manually via code - call invalidateIntrinsicContentSize() on YourCustomView object

how to make scrollable label using scroll view?

I want to make something like article to read view controller that contains UIImage and Label nested in the scroll view.
I want if the article content is not to big, the scrolling function is disabled, but if the content is big, I want it can be scrolled and it can be scrolled as the same height of the label content height.
(if the content article is not too much, then i don't need to scroll)
but if the content is a lot then I expect that I can scroll. at the moment, I can scroll if the content is big like below
I have tried to set the bottom anchor like my code below but it doesn't work. here is the autolayout I use for the bottom label contraint andthe view hierarchy
class NotificationDetailVC: UIViewController {
#IBOutlet weak var notificationTitleLabel: UILabel!
#IBOutlet weak var notificationImage: UIImageView!
#IBOutlet weak var notificationDateLabel: UILabel!
#IBOutlet weak var notificationScrollView: UIScrollView!
#IBOutlet weak var notificationContentLabel: UILabel!
var dataNotification : [String:Any]?
override func viewDidLoad() {
super.viewDidLoad()
notificationScrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: notificationContentLabel.bottomAnchor).isActive = true
}
}
Don't make bottom constraint to greater than. just set it equal to.
Scroll view has a content view which contains all scrollable view. You have to set height, width and x,y position of content view.
Set top, bottom, leading and trailing constraint to content view.
Scrollview's scrolling directions depands on height and width of contantview. if you set content view's width equal to scrollview than scrollview is not scrollable in horizontal direction. Same for height , if you set content view's height equal to scrollview's height than scrollview is not scrollable in vertical direction.
In your case you have to set content view's width equal to scrollview's width therefore scrollview is not scrollable in horizontal direction. Content view automatically get height according to subviews(In your case UIimageview and uilabel). so set height, top, bottom constraint of imageview and top and bottom constraints of uilabel because uilabel height automatically set by it's text. Set number of line = 0 in UILabel.
Set Label bottom constaint equal to any constant height (say 20) & number of lines = 0.
as you are using scrollview then you have to give height to the imageview. You don't need to worry about label height bcz its heigght will be calculated by its content height.
Now your scrollviewContentHeight = imgView.height + all labels content height. Make sure each component (imageview & every label) have top and bottom constraint.
That's all you have to do.

Resources