How Can I Make CAGradientLayer Subclass Backing Layer of UIView Subclass - calayer

I have created a subclass of CAGradientLayer with desired values for colors, startPoint, and endPoint. It looks like this:
public final class GradientBackgroundLayer: CAGradientLayer {
// MARK: - Properties
private let leftColor = UIColor(red: 0.96,
green: 0.64,
blue: 0.42,
alpha: 1.00)
private let rightColor = UIColor(red: 0.88,
green: 0.36,
blue: 0.38,
alpha: 1.00)
// MARK: - Initialization
public override init() {
super.init()
configure()
}
public override init(layer: Any) {
super.init(layer: layer)
configure()
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Helpers
private func configureColors() {
colors = [leftColor,
rightColor]
}
private func configureLocations() {
gradientLayer.locations = [0.1,
0.9]
}
private func configure() {
configureColors()
configureLocations()
}
}
I also have a subclass of UIView that I want to have an instance of GradientBackgroundLayer as its backing layer. It looks like this:
public final class GradientView: UIView {
// MARK: - Properties
override public class var layerClass: AnyClass {
return GradientBackgroundLayer.self
}
// MARK: - Initialization
public override init(frame: CGRect) {
super.init(frame: frame)
}
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I call call the following initializer in a Playground file:
let view = GradientView(frame: CGRect(x: 0, y: 0, width: 400, height: 100))
I get a black view. I see it update if I change the layer's backgroundColor, but I can't figure out what I am doing incorrectly to cause the layer to not display the gradient.

It turns out that I forgot to add .cgColor to the gradient's colors when I set the colors property of GradientBackgroundLayer. Adding the required .cgColor to the UIColor instances solved my issue.

Related

Cocoa FocusRing gets distorted when its view gets resized

I display an NSWindow that contains an NSbutton. The button can be focussed with the tab key for keyboard interaction, the standard focus ring is shown around the button.
The window has a fixed aspect ratio and can be resized as a whole. When this happens, all content should resize, i.e. the button gets larger or smaller.
This works fine so far. However, the focus ring does only kind of resize properly: the general position and its width are fine, but its heigth does not match the button’s heigth anymore.
Here is how the window initially looks like:
...and here after the window is resized:
You'll probably notice that the text gets slightly disproportional as well, but to no such extent as the focus ring is misplaced.
What steps do I have to take to have the focus ring match its corresponding button at all zoom levels?
Here is my code:
import Cocoa
#main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
window.contentViewController = MyViewController(size: NSSize(width: 200, height: 72))
window.minSize = CGSize(width: 200, height: 100)
window.aspectRatio = NSMakeSize(2,1)
}
}
class MyViewController: NSViewController {
public init(size: NSSize) {
super.init(nibName: nil, bundle: nil)
self.view = MyView(frame: NSRect(x: 0, y: 0, width: size.width, height: size.height))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class MyView: NSView {
let initialFirstResponder: NSView
public override init(frame: NSRect) {
let textField = NSTextField(labelWithString: "Textfield")
textField.isEditable = false
textField.frame = NSRect(x: 20, y: 36, width: 100, height: 15)
let switchButton = NSButton(title: "Switch", target: nil, action: nil)
switchButton.frame = NSRect(x: 100, y: 31, width: 80, height: 20)
self.initialFirstResponder = switchButton
super.init(frame: frame)
self.bounds = frame
self.postsBoundsChangedNotifications = true
NotificationCenter.default.addObserver(forName: NSView.frameDidChangeNotification, object: self, queue: OperationQueue.main, using: {(note) in
self.setBoundsSize(NSSize(width: 200, height: 72)) // 28px for titlebar
print("Frame nun \(self.frame.width) x \(self.frame.height)")
})
self.addSubview(textField)
self.addSubview(switchButton)
self.autoresizesSubviews = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

NSTextView not visible

I want to create a Bordered Rectangle with a Text/Label positioned in the top-left with AppKit that looks like:
The text view should have the same border and background color as the rectangle's border, and a padding around the actual text, not a fixed width/height constant.
I rewrote the above view in AppKit with NSTextView, but I can't see the textview, just the border:
class JumperView: NSView {
let label: String
private let borderWidth: CGFloat = 4
private let borderColor = NSColor(red: 250 / 255, green: 255 / 255, blue: 0 / 255, alpha: 1).cgColor
private let cornerRadius: CGFloat = 8
private var labelView: NSTextView!
required init(frame: NSRect, label: String) {
self.label = label
super.init(frame: frame)
wantsLayer = true
layer!.borderWidth = borderWidth
layer!.borderColor = borderColor
layer!.cornerRadius = cornerRadius
setupLabelView()
self.translatesAutoresizingMaskIntoConstraints = false
self.labelView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.labelView.topAnchor.constraint(equalTo: self.topAnchor),
self.labelView.leftAnchor.constraint(equalTo: self.leftAnchor),
])
}
private func setupLabelView() {
labelView = NSTextView()
labelView.string = label
labelView.font = .systemFont(ofSize: 24, weight: .bold)
// labelView.textContainerInset = .init(width: 8, height: 4)
labelView.wantsLayer = true
labelView.layer!.borderWidth = borderWidth
labelView.layer!.borderColor = borderColor
labelView.layer!.cornerRadius = cornerRadius
labelView.layer!.backgroundColor = borderColor
labelView.backgroundColor = NSColor(cgColor: borderColor)!
addSubview(labelView)
}
override init(frame frameRect: NSRect) {
fatalError()
}
required init?(coder: NSCoder) {
fatalError()
}
}
I'm not sure why the text view isn't showing up. Would appreciate the help!

How do I set a pin on Current Location, use Pin as Geofence, alert user onEnter, then reset all / start over again?

I'm a product designer trying to spin up a location-based reminder concept to validate a pretty early stage idea. I could prototype all of these screens in Figma, but real-time Location and a contextual alert are foundational to my hypothesis, so here I am pulling my hair out trying to accomplish this in SwiftUI 😅
https://www.figma.com/proto/jxYwPbqe4oqsXBbf6CZDDi/Location-Reminder-Spec?page-id=0%3A1&node-id=1%3A189&viewport=161%2C384%2C0.12701110541820526&scaling=scale-down
I've been piecing this together from a variety of tutorials (Ray Wenderlich, Hacking Swift, Apple's Swift UI) and snippets about Location etc. but most of the tutorials have more objects and views and other stuff that I don't need which is complicating my scrappy MVP collage approach.
So far, I've got an MKMapView centered on my the coordinates of the User's Location and I've got my button displayed.
I'm wondering
How to create a CLCircularRegion using the coordinates from user location?
How to place a MKMapAnnotation on the Map using the coordinates from the user location and how to remove the annotation after pressing a button inside the notification?
With the above, I think I'll be able to sort out how to fire the notification and show the notification onEnter.
I've got 4 files in the project so far...
LocationManager.swift
import Foundation
import MapKit
import CoreLocation
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
#Published var region = MKCoordinateRegion()
private let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last.map{
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
}
}
}
extension CLLocation {
var latitude: Double {
return self.coordinate.latitude
}
var longitude: Double {
return self.coordinate.longitude
}
}
ContentView.swift
import CoreLocation
import MapKit
import SwiftUI
struct ContentView: View {
#StateObject var manager = LocationManager()
#State private var locations = [MKPointAnnotation] ()
var body: some View {
VStack {
HStack {
Text("Location Reminder")
.font(.largeTitle)
.bold()
.foregroundColor(.black)
Spacer()
}
.padding()
ZStack (alignment: .bottomTrailing) {
Map(coordinateRegion: $manager.region,
showsUserLocation: true)
.ignoresSafeArea()
ReminderButton()
.padding()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ReminderButton.swift
import Foundation
import SwiftUI
struct ReminderButton: View {
var body: some View {
VStack {
Spacer()
Button(action: {
}) {
HStack {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 40, height: 40)
Text("Add a Reminder")
}
.padding()
.background(Color.white)
.cornerRadius(40)
.shadow(color: .gray, radius: 0.2, x: 1, y: 1)
}
}
}
}
I have an empty file that I've named Geofence.swift where I plan on creating a CLCircularRegion and setting up the notifications / alerts onEnter etc.
Appreciate any and all help / advice / suggestions. Also please LMK if there are ways to improve my post and/or if it belongs elsewhere.
import SwiftUI
import MapKit
import CoreLocation
struct SingleLocationView: View {
#StateObject var manager = LocationManager()
#State private var locations = [PinnedLocation] ()
#State var userTrackingMode: MapUserTrackingMode = .follow
let radius: CGFloat = 160 //meters approx 0.1 miles
var body: some View {
VStack {
HStack {
VStack{
Text("Location Reminder")
.font(.largeTitle)
.bold()
.foregroundColor(.black)
}
Spacer()
}
.padding()
ZStack (alignment: .bottomTrailing) {
GeometryReader{ geo in
Map(coordinateRegion: $manager.region, interactionModes: .all, showsUserLocation: true, userTrackingMode: $userTrackingMode, annotationItems: locations, annotationContent: { annotation in
MapAnnotation(coordinate: annotation.coordinate, anchorPoint: CGPoint(x: 0.5, y: 0.5), content: {
//Use this vs Circle because your map is a rectangle circumference is an oval
//Maybe a stretched Circle could be a little more accurate
RoundedRectangle(cornerRadius: 25)
.foregroundColor(.white)
.overlay(
Image(systemName: "mappin").foregroundColor(.red))
// The screen vertical and horizontal multiplied by your radius divided Map vertial/horizontal span in meters
.frame(width: (geo.size.width/manager.regionSpanInLongitudeDeltaMeters.value) * radius, height: (geo.size.height/manager.regionSpanInLatitudeDeltaMeters.value) * radius, alignment: .center)
})
})
.ignoresSafeArea()
}
ReminderButton(locations: $locations).environmentObject(manager)
.padding()
}
}
}
}
struct SingleLocationView_Previews: PreviewProvider {
static var previews: some View {
SingleLocationView()
}
}
//You MKAnnotaion is a Protocol you need to create your own class
//MKPointAnnotation is a MKShape for use with MKMapView
class PinnedLocation: NSObject, MKAnnotation, Identifiable{
var title: String?
var subtitle: String?
var coordinate: CLLocationCoordinate2D
init(title: String? = nil, subtitle: String? = nil, coordinate: CLLocationCoordinate2D) {
self.title = title
self.subtitle = subtitle
self.coordinate = coordinate
}
///Get the CLCircularRegion with the given radius
func getCLCircularRegion(radius: CGFloat) -> CLCircularRegion{
CLCircularRegion(center: self.coordinate, radius: radius, identifier: "\(self.id)")
}
}
extension UnitLength{
//This is approx do research on degrees to meters
static let degree = UnitLength(symbol: "degree", converter: UnitConverterLinear(coefficient: 111111))
}
class LocationManager: NSObject, CLLocationManagerDelegate, ObservableObject {
#Published var region = MKCoordinateRegion()
//If you do it the way you were doing it get the most current location from here to append to your array
#Published var lastLocation: CLLocation = CLLocation()
///Region gives span in degrees. This is a rough conversion
var regionSpanInLatitudeDeltaMeters: Measurement<UnitLength>{
Measurement(value: region.span.latitudeDelta, unit: UnitLength.degree).converted(to: .meters)
}
///Region gives span in degrees. This is a rough conversion
var regionSpanInLongitudeDeltaMeters: Measurement<UnitLength>{
Measurement(value: region.span.longitudeDelta, unit: UnitLength.degree).converted(to: .meters)
}
private let manager = CLLocationManager()
override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
//Don't need it SwiftUI does this for you
//manager.requestWhenInUseAuthorization()
//manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locations.last.map{
region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: $0.coordinate.latitude, longitude: $0.coordinate.longitude),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
}
lastLocation = locations.last!
}
}
extension CLLocationCoordinate2D{
var annotation: PinnedLocation{
PinnedLocation(coordinate: CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude))
}
}
struct ReminderButton: View {
//The button joins your location manager with the locations displayed. It needs access to both
#EnvironmentObject var manager: LocationManager
#Binding var locations: [PinnedLocation]
var body: some View {
VStack {
Spacer()
Button(action: {
//Append the current center/location to your list of annotions
locations.append(manager.region.center.annotation)
}) {
HStack {
Image(systemName: "plus.circle.fill")
.resizable()
.frame(width: 40, height: 40)
Text("Add a Reminder")
}
.padding()
.background(Color.white)
.cornerRadius(40)
.shadow(color: .gray, radius: 0.2, x: 1, y: 1)
}
}
}
}

swift4 CAKeyframeAnimation not work

I used layer animations on a view, but I couldn't work at swift4
My view override the layerClass property
public override class var layerClass: Swift.AnyClass {
return CoordLayer.self
}
And custom key creation CAKeyframeAnimation
let animation = CAKeyframeAnimation(keyPath: "moveX")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)
animation.values = values
animation.keyTimes = times.map{ NSNumber(value: $0) }
animation.duration = duration
animation.fillMode = kCAFillModeForwards
animation.delegate = self
self.layer.add(animation, forKey: "moveX")
The CoordLayer Class
class CoordLayer: CALayer {
override class func needsDisplay(forKey key: String) -> Bool {
if "moveX" == key {
return true // ..... swift4 No call ....
}
return super.needsDisplay(forKey: key)
}
}
override func display(){
// .......some custom operations......
// ....... swift4 No call .......
}
}
The view has no animation effect swift4
KeyFrameAnimation is working as expected in swift 4 also :
Please check the implementation, you need some correction in your implementation.
Let's see it.
Define Controller :
import UIKit
// Controller
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addView()
}
private let myView : MyView = {
let view = MyView()
view.translatesAutoresizingMaskIntoConstraints = false // enable auto layout
view.backgroundColor = .red
return view
}() // execute closure
private func addView() {
view.addSubview(myView)
NSLayoutConstraint.activate([
myView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), // leading
myView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), // top
myView.widthAnchor.constraint(equalTo: view.safeAreaLayoutGuide.widthAnchor, multiplier: 0.2), // width = 20 % of view width
myView.heightAnchor.constraint(equalTo: myView.widthAnchor, multiplier: 1.0) // height = width
])
}
}
Define MyView :
// View
class MyView : UIView {
// draw view
override func draw(_ rect: CGRect) {
super.draw(rect)
}
// layerClass
public override class var layerClass: Swift.AnyClass {
return CoordLayer.self
}
}
Define CoordLayer :
// Layer
class CoordLayer : CALayer {
override func display() {
// some custom operations
backgroundColor = UIColor.red.cgColor // to visualize
customAnimation() // add animation
print("Display")
}
override class func needsDisplay(forKey key: String) -> Bool {
if key == "transform.translation.x" {
return true
}
return super.needsDisplay(forKey: key)
}
// add animation
private func customAnimation() {
let values = [100,150,200,250]
let times : [NSNumber] = [0.0, 0.25, 0.5, 1.0] // not necessary always
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x") // moving view along x direction
animation.timingFunctions = [CAMediaTimingFunction(name: kCAMediaTimingFunctionDefault)] // array of timing function
animation.values = values // taking the animated property values for animation
animation.keyTimes = times // define the timing array
animation.duration = 5.0 // CFTimeInterval
animation.isRemovedOnCompletion = false // do not remove the animation effect, no state changes.
animation.fillMode = kCAFillModeForwards
animation.delegate = self
// add animation on coordLayer
self.add(animation, forKey: "animation")
}
}
Animation Delegate :
extension CoordLayer : CAAnimationDelegate {
func animationDidStart(_ anim: CAAnimation) {
print("Animation started")
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
print("Animation stopped")
}
}
Correction in your implementation :
(1) Instead of "moveX", use "transform.translation.x"
(2) Give some animation duration, animation.duration = 5.0 // CFTimeInterval
(3) animation.isRemovedOnCompletion = false , so that after animation, view will not move it's beginning position.
You can add animation logic in MyView also because you are adding animation on layer. Animation Delegate can be conformed by MyView.(it's upto you).
You can check out the implementation here.
Thanks. :-)

Masonry UITableview Cell : Autolayout errors for multiline label

I am learning auto layout in iOS8 by using SnapKit . I got lot of errors while applying constraints to cell subviews. below is the code used as subview to cell.contentview.
class FanFeedDynamicCellView: UIView{
var fanProfileImageView:UIImageView?
var fanNameLabel:UILabel?
var contentLabel:UILabel?
var thumbnailImageView:UIImageView?
var spacierView_FanProfile:UIView?
override init(frame: CGRect) {
super.init(frame : frame)
setupViewProperties()
}
convenience init () {
self.init(frame:CGRect.zero)
}
required init(coder aDecoder: NSCoder) {
fatalError("This class does not support NSCoding")
}
func setupViewProperties()
{
//1 add fanProfileImageView
fanProfileImageView = UIImageView()
fanProfileImageView!.image = UIImage(named: "avatar")
// setBorder(fanProfileImageView!)
self.addSubview(fanProfileImageView!)
//2 add Fan Name Label
fanNameLabel = UILabel()
fanNameLabel!.lineBreakMode = .ByTruncatingTail
fanNameLabel!.numberOfLines = 1
fanNameLabel!.textAlignment = .Left
fanNameLabel!.textColor = UIColor.blackColor()
fanNameLabel!.backgroundColor = UIColor(red: 0, green: 0, blue: 1, alpha: 0.1) // light blue
self.addSubview(fanNameLabel!)
//3 add ContentLabel
contentLabel = UILabel()
contentLabel!.lineBreakMode = .ByTruncatingTail
contentLabel!.numberOfLines = 0
contentLabel!.textAlignment = .Left
contentLabel!.textColor = UIColor.darkGrayColor()
contentLabel!.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.1) // light red
self.addSubview(contentLabel!)
//4 add Thumbnail View
thumbnailImageView = UIImageView()
// setBorder(thumbnailImageView!)
thumbnailImageView!.contentMode = .ScaleAspectFit
thumbnailImageView!.image = UIImage(named: "avatar")
self.addSubview(thumbnailImageView!)
updateFonts()
//Constraints for subviews
//setupConstraintsForProperties()
}
func updateFonts()
{
fanNameLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
contentLabel!.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption2)
}
override func updateConstraints()
{
let padding:UIEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)
fanProfileImageView!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.snp_top).offset(padding.top)
make.left.equalTo(self.snp_left).offset(padding.left)
make.width.height.equalTo(60.0)
make.bottom.lessThanOrEqualTo(thumbnailImageView!.snp_top).offset(-padding.bottom)
}
fanNameLabel!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self.snp_top).offset(padding.top)
make.left.equalTo(fanProfileImageView!.snp_right).offset(padding.right)
make.right.equalTo(self.snp_right).offset(-padding.right)
// make.bottom.lessThanOrEqualTo(contentLabel!.snp_top).offset(-padding.top)
make.height.equalTo(20)
}
contentLabel!.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanNameLabel!.snp_bottom).offset(padding.top)
make.left.equalTo(fanProfileImageView!.snp_right).offset(padding.left)
make.right.equalTo(self.snp_right).offset(-padding.right)
// make.bottom.lessThanOrEqualTo(thumbnailImageView!.snp_top).offset(-padding.bottom)
// make.height.greaterThanOrEqualTo(20)
}
thumbnailImageView!.snp_makeConstraints { (make) -> Void in
make.top.greaterThanOrEqualTo(contentLabel!.snp_bottom).offset(padding.top)
// make.left.equalTo(padding.left)
make.bottom.lessThanOrEqualTo(-padding.bottom)
make.height.greaterThanOrEqualTo(20)
make.centerX.equalTo(self.snp_centerX) }
super.updateConstraints()
}
func setBorder(cView:UIView) -> UIView
{
let cLayer : CALayer = cView.layer
cLayer.borderColor = UIColor.redColor().CGColor
cLayer.borderWidth = 0.5
return cView
}
override func layoutSubviews() {
super.layoutSubviews()
fanNameLabel!.contentHuggingPriorityForAxis(.Vertical)
fanNameLabel!.contentCompressionResistancePriorityForAxis(.Vertical)
contentLabel!.contentHuggingPriorityForAxis(.Vertical)
contentLabel!.contentCompressionResistancePriorityForAxis(.Vertical)
}
The output would be the same as attached image . here we use on profile Image in LeftSide . User Name on top of label. Content label marked in light orange color would be the multiline. below this i attached the ImageView. when i scroll the tableview the height of the cell is unpredictable(layout changes automatically). would help me to correct the constraint to achieve the this output. For first launch the multiline cell would be in One line . once i goes to invisible the come again visible it adopt for full label content
You add more constraints than you had to. Also you don't really need to use contentHuggingPriority and contentCompressionResistance to achieve what you want.
This is how to make it work:
FanFeedDynamicCellView
class FanFeedDynamicCellView: UIView {
let fanProfileImageView = UIImageView()
let fanNameLabel = UILabel()
let contentLabel = UILabel()
let thumbnailImageView = UIImageView()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews() {
fanProfileImageView.image = UIImage(named: "avatar")
addSubview(fanProfileImageView)
fanNameLabel.lineBreakMode = .ByTruncatingTail
fanNameLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
fanNameLabel.numberOfLines = 1
fanNameLabel.textAlignment = .Left
fanNameLabel.textColor = UIColor.blackColor()
fanNameLabel.backgroundColor = UIColor(red: 0, green: 0, blue: 1, alpha: 0.1) // light blue
addSubview(fanNameLabel)
contentLabel.lineBreakMode = .ByTruncatingTail
contentLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleCaption2)
contentLabel.numberOfLines = 0
contentLabel.textAlignment = .Left
contentLabel.textColor = UIColor.darkGrayColor()
contentLabel.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 0.1) // light red
addSubview(contentLabel)
thumbnailImageView.contentMode = .ScaleAspectFit
thumbnailImageView.image = UIImage(named: "thumbnail.jpg")
thumbnailImageView.backgroundColor = UIColor(red: 0, green: 1, blue: 0, alpha: 0.2) // light green
addSubview(thumbnailImageView)
}
func setupConstraints() {
let padding:UIEdgeInsets = UIEdgeInsetsMake(10, 10, 10, 10)
fanProfileImageView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(padding.top)
make.left.equalTo(padding.left)
make.width.height.equalTo(60)
make.bottom.lessThanOrEqualTo(-padding.bottom)
}
fanNameLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanProfileImageView)
make.left.equalTo(fanProfileImageView.snp_right).offset(padding.right)
make.right.equalTo(-padding.right)
}
contentLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(fanNameLabel.snp_bottom).offset(padding.top)
make.left.right.equalTo(fanNameLabel)
}
thumbnailImageView.snp_makeConstraints { (make) -> Void in
make.top.greaterThanOrEqualTo(contentLabel.snp_bottom).offset(padding.top)
make.top.greaterThanOrEqualTo(fanProfileImageView.snp_bottom).offset(padding.top)
make.left.equalTo(fanProfileImageView)
make.right.equalTo(fanNameLabel)
make.bottom.equalTo(-padding.bottom)
}
}
}
I don't know why you created a UIView subclass instead of a UITableViewCell subclass for this, but I kept it that way and just added this view to a custom UITableViewCell subclass
FanFeedTableViewCell
This only adds the FanFeedDynamicCellView to its contentView:
class FanFeedTableViewCell: UITableViewCell {
let fanFeedView = FanFeedDynamicCellView()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .Subtitle, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupView() {
contentView.addSubview(fanFeedView)
fanFeedView.snp_makeConstraints { (make) -> Void in
make.edges.equalTo(contentView)
}
}
}
And that's it! Now just use this FanFeedTableViewCell in your UITableView and set the texts and images in cellForRowAtIndexPath.
Don't forget to do this with your table view to enable dynamic cell heights:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100 // or whatever your estimated row height is. This does not have to be precise
This is how it looks:

Resources